import { describe, expect, it, vi } from "vitest"; import { buildClaudeOAuthWriteInput, buildSetupTokenLoginTransport, createProductionSetupTokenSandboxProvider, createSetupTokenSecretWriter, createWorkerBoundLoginPtyOpener, type SetupTokenSandboxProvider, } from "./setup-token-transport-binding.js"; import { SetupTokenSessionService, isTerminalSessionState, type SetupTokenCleanupIdentity, type SetupTokenCleanupRecord, type SetupTokenCleanupStore, type SetupTokenLoginProcess, type SetupTokenSessionScope, type SetupTokenSessionState, } from "./setup-token-session.js"; import type { LoginPtySessionOpener } from "@paperclipai/adapter-utils/login-pty-transport"; import { CLAUDE_SETUP_TOKEN_COMMAND, SETUP_TOKEN_AFTER_ANCHOR, SETUP_TOKEN_BEFORE_ANCHOR, } from "@paperclipai/adapter-claude-local/server"; // The owner scope for one login session. The per-owner session cap is one, so one // scope holds one live session. const SCOPE: SetupTokenSessionScope = { companyId: "company-1", ownerUserId: "user-1", adapterType: "claude_local", environmentId: "env-1", }; /** Waits for the pending microtasks and macrotasks to settle. */ function flush(): Promise { return new Promise((resolve) => setImmediate(resolve)); } /** * A controllable fake sandbox. It records the opened command and every released * lease id. It drives the login process to a terminal outcome through the exit * promise, so a test simulates a process failure, a timeout, or a cancel. */ function createFakeSandbox() { const openedCommands: string[] = []; const releasedLeaseIds: string[] = []; let acquireCount = 0; let killed = false; let resolveExit: (value: { exitCode: number | null }) => void = () => {}; const exit = new Promise<{ exitCode: number | null }>((resolve) => { resolveExit = resolve; }); const openPtySession: LoginPtySessionOpener = async (command) => { openedCommands.push(command); return { onData(): void {}, write(): void {}, wait: () => exit, kill(): void { killed = true; resolveExit({ exitCode: 137 }); }, async close(): Promise {}, }; }; const provider: SetupTokenSandboxProvider = { async acquire() { acquireCount += 1; return { leaseId: `lease-${acquireCount}`, openPtySession }; }, async release(leaseId) { releasedLeaseIds.push(leaseId); }, }; return { provider, openedCommands, releasedLeaseIds, get acquireCount() { return acquireCount; }, get killed() { return killed; }, finishProcess(exitCode: number | null): void { resolveExit({ exitCode }); }, }; } /** * An in-memory cleanup store that records every write. It stands in for the * durable database-backed store, so a test asserts the service runs its cleanup * against the injected store. */ function createRecordingStore() { const rows = new Map(); const removed: string[] = []; const stateWrites: Array<{ sessionId: string; state: SetupTokenSessionState }> = []; let reapable: SetupTokenCleanupRecord[] = []; const store: SetupTokenCleanupStore = { async record(record) { rows.set(record.sessionId, { ...record }); }, async markState(identity: SetupTokenCleanupIdentity, state) { stateWrites.push({ sessionId: identity.sessionId, state }); const row = rows.get(identity.sessionId); if (row) row.state = state; }, async remove(identity) { removed.push(identity.sessionId); rows.delete(identity.sessionId); }, async listReapable() { return reapable; }, async consumeStoredClaim() { return null; }, async cancelDurable() { return null; }, async findActiveDurable(key, now) { for (const row of rows.values()) { if ( row.companyId === key.companyId && row.ownerUserId === key.ownerUserId && row.adapterType === key.adapterType && !isTerminalSessionState(row.state) && row.deadline > now ) { return { ...row }; } } return null; }, }; return { store, rows, removed, stateWrites, setReapable(records: SetupTokenCleanupRecord[]): void { reapable = records; }, }; } /** A permissive rate limiter, so the start path never rate-limits in a test. */ const allowAllRateLimiter = { consume: () => ({ allowed: true, retryAfterSeconds: 0 }) }; function buildService(input: { sandbox: SetupTokenSandboxProvider; store: SetupTokenCleanupStore; ttlMs?: number; }) { const transport = buildSetupTokenLoginTransport({ sandbox: input.sandbox, store: input.store, }); const service = new SetupTokenSessionService({ factory: transport.factory, leases: transport.leases, store: transport.store, // The secret write stays a fail-closed default here. This phase does not bind // the owner-bound secret writer. completeCredential: async () => { throw new Error("The credential write is not bound in this phase."); }, rateLimiter: allowAllRateLimiter, ttlMs: input.ttlMs ?? 5 * 60_000, }); return { service, transport }; } describe("setup-token production transport binding", () => { it("creates a live session with the transport bound and opens only the fixed command", async () => { const sandbox = createFakeSandbox(); const store = createRecordingStore(); const { service } = buildService({ sandbox: sandbox.provider, store: store.store }); const started = await service.start(SCOPE); await flush(); // The start returned a session id instead of the fixed 503. expect(typeof started.sessionId).toBe("string"); expect(started.sessionId.length).toBeGreaterThan(0); // The lease manager acquired one production lease. expect(sandbox.acquireCount).toBe(1); // The factory opened the pseudo-terminal with only the fixed command. expect(sandbox.openedCommands).toEqual([CLAUDE_SETUP_TOKEN_COMMAND]); // The service persisted the non-secret cleanup record at start. expect(store.rows.get(started.sessionId)?.leaseId).toBe("lease-1"); await service.shutdown(); }); it("releases the lease and cleans the durable store on a process failure", async () => { const sandbox = createFakeSandbox(); const store = createRecordingStore(); const { service } = buildService({ sandbox: sandbox.provider, store: store.store }); const started = await service.start(SCOPE); await flush(); // The login process ends with a non-zero exit code. sandbox.finishProcess(1); await flush(); await flush(); expect(sandbox.releasedLeaseIds).toEqual(["lease-1"]); expect(store.removed).toContain(started.sessionId); expect(store.rows.has(started.sessionId)).toBe(false); }); it("releases the lease and cleans the durable store on a cancel", async () => { const sandbox = createFakeSandbox(); const store = createRecordingStore(); const { service } = buildService({ sandbox: sandbox.provider, store: store.store }); const started = await service.start(SCOPE); await flush(); await service.cancel(started.sessionId, SCOPE); await flush(); expect(sandbox.releasedLeaseIds).toEqual(["lease-1"]); expect(sandbox.killed).toBe(true); expect(store.removed).toContain(started.sessionId); }); it("releases the lease and cleans the durable store on a timeout expiry", async () => { const sandbox = createFakeSandbox(); const store = createRecordingStore(); const { service } = buildService({ sandbox: sandbox.provider, store: store.store }); const started = await service.start(SCOPE); await flush(); await service.expire(started.sessionId, SCOPE); await flush(); expect(sandbox.releasedLeaseIds).toEqual(["lease-1"]); expect(store.removed).toContain(started.sessionId); }); it("cancels every live session and releases the lease on shutdown", async () => { const sandbox = createFakeSandbox(); const store = createRecordingStore(); const { service } = buildService({ sandbox: sandbox.provider, store: store.store }); const started = await service.start(SCOPE); await flush(); await service.shutdown(); await flush(); expect(sandbox.releasedLeaseIds).toEqual(["lease-1"]); expect(store.removed).toContain(started.sessionId); expect(service.activeSessionCount()).toBe(0); }); it("ignores a runtime-supplied command and always opens the fixed command", async () => { const sandbox = createFakeSandbox(); const store = createRecordingStore(); // A caller forces an alternate command through the dependency object. The // dependency type carries no command field, so this cast simulates a // runtime-supplied value from a route, a body, or a configuration. The // binding must ignore it and open only the fixed command. const deps = { sandbox: sandbox.provider, store: store.store, command: "echo pwned", } as unknown as Parameters[0]; const transport = buildSetupTokenLoginTransport(deps); const service = new SetupTokenSessionService({ factory: transport.factory, leases: transport.leases, store: transport.store, completeCredential: async () => { throw new Error("The credential write is not bound in this phase."); }, rateLimiter: allowAllRateLimiter, ttlMs: 5 * 60_000, }); const started = await service.start(SCOPE); await flush(); // The factory opened the pseudo-terminal with only the fixed command. The // supplied alternate value never reached the command. expect(sandbox.openedCommands).toEqual([CLAUDE_SETUP_TOKEN_COMMAND]); expect(sandbox.openedCommands).not.toContain("echo pwned"); void started; await service.shutdown(); }); it("keeps the durable cleanup record reapable when a restart release fails", async () => { // The production provider drives a restart release by id. The provider driver // release rejects, so the reaper must keep the durable cleanup record. const releaseRunLease = vi.fn(async () => { throw new Error("remote release failed"); }); const provider = createProductionSetupTokenSandboxProvider({ environments: { getById: async () => ({ id: "env-1", driver: "sandbox", status: "active" }) as never, getLeaseById: async () => ({ id: "lease-orphan", environmentId: "env-1" }) as never, releaseLease: async () => ({}) as never, }, environmentRuntime: { acquireRunLease: vi.fn(), getDriver: () => ({ releaseRunLease }) as never, }, openLivePtySession: async () => { throw new Error("must not open a pty for a restart release"); }, }); const store = createRecordingStore(); const { service } = buildService({ sandbox: provider, store: store.store }); // A crash left a durable record whose lease the reaper must free. The // in-memory lease map is empty, as after a restart. const orphan: SetupTokenCleanupRecord = { sessionId: "orphan-session", companyId: SCOPE.companyId, ownerUserId: SCOPE.ownerUserId, adapterType: SCOPE.adapterType, environmentId: SCOPE.environmentId, leaseId: "lease-orphan", deadline: 0, state: "failed", boundAt: null, }; store.rows.set(orphan.sessionId, { ...orphan }); store.setReapable([orphan]); const result = await service.reap(Date.now()); // The remote release ran and failed, so the reaper counts a failure and keeps // the durable cleanup record for a later retry. expect(releaseRunLease).toHaveBeenCalledTimes(1); expect(result).toEqual({ released: 0, failed: 1 }); expect(store.removed).not.toContain("orphan-session"); expect(store.rows.has("orphan-session")).toBe(true); }); it("releases a leftover lease by id on a restart reap", async () => { const sandbox = createFakeSandbox(); const store = createRecordingStore(); const { service } = buildService({ sandbox: sandbox.provider, store: store.store }); // A crash left a durable record whose lease the reaper must free after a // restart. No live session references it. const orphan: SetupTokenCleanupRecord = { sessionId: "orphan-session", companyId: SCOPE.companyId, ownerUserId: SCOPE.ownerUserId, adapterType: SCOPE.adapterType, environmentId: SCOPE.environmentId, leaseId: "lease-orphan", deadline: 0, state: "failed", boundAt: null, }; store.setReapable([orphan]); const result = await service.reap(Date.now()); expect(result).toEqual({ released: 1, failed: 0 }); expect(sandbox.releasedLeaseIds).toEqual(["lease-orphan"]); expect(store.removed).toContain("orphan-session"); }); }); describe("production sandbox provider", () => { it("fails closed before it acquires a lease when the live opener is not bound", async () => { const acquireRunLease = vi.fn(); const provider = createProductionSetupTokenSandboxProvider({ environments: { getById: async () => ({ id: "env-1", driver: "sandbox", status: "active" }) as never, getLeaseById: async () => null, releaseLease: async () => ({}) as never, }, environmentRuntime: { acquireRunLease, getDriver: () => null, }, // No `openLivePtySession`: the live path lands with the // characterization test. }); await expect(provider.acquire({ scope: SCOPE, deadline: Date.now() + 1000 })).rejects.toMatchObject({ status: 503, }); // The provider held no lease, so it never acquired one. expect(acquireRunLease).not.toHaveBeenCalled(); }); it("fails closed for a local environment", async () => { const provider = createProductionSetupTokenSandboxProvider({ environments: { getById: async () => ({ id: "env-1", driver: "local", status: "active" }) as never, getLeaseById: async () => null, releaseLease: async () => ({}) as never, }, environmentRuntime: { acquireRunLease: vi.fn(), getDriver: () => null, }, openLivePtySession: async () => { throw new Error("must not reach the opener for a local environment"); }, }); await expect(provider.acquire({ scope: SCOPE, deadline: Date.now() + 1000 })).rejects.toMatchObject({ status: 503, }); }); it("acquires a lease and binds the live opener when it is present", async () => { const openPtySession: LoginPtySessionOpener = async () => ({ onData(): void {}, write(): void {}, wait: async () => ({ exitCode: 0 }), kill(): void {}, async close(): Promise {}, }); const releaseRunLease = vi.fn(async () => null); const deadline = Date.now() + 1000; const provider = createProductionSetupTokenSandboxProvider({ environments: { getById: async () => ({ id: "env-1", name: "Sandbox", driver: "sandbox", status: "active" }) as never, getLeaseById: async () => null, releaseLease: async () => ({}) as never, }, environmentRuntime: { acquireRunLease: async () => ({ environment: { id: "env-1", driver: "sandbox" }, // The runtime bounds the lease expiry to the session deadline. lease: { id: "lease-live", expiresAt: new Date(deadline - 1) }, leaseContext: {}, }) as never, getDriver: () => ({ releaseRunLease }) as never, }, openLivePtySession: async () => openPtySession, }); const acquired = await provider.acquire({ scope: SCOPE, deadline }); expect(acquired.leaseId).toBe("lease-live"); await provider.release(acquired.leaseId); expect(releaseRunLease).toHaveBeenCalledTimes(1); }); it("forwards the session deadline and records a lease expiry at or before it", async () => { const openPtySession: LoginPtySessionOpener = async () => ({ onData(): void {}, write(): void {}, wait: async () => ({ exitCode: 0 }), kill(): void {}, async close(): Promise {}, }); const releaseRunLease = vi.fn(async () => null); const deadline = Date.now() + 60_000; let forwardedExpiresAt: Date | null | undefined; const provider = createProductionSetupTokenSandboxProvider({ environments: { getById: async () => ({ id: "env-1", name: "Sandbox", driver: "sandbox", status: "active" }) as never, getLeaseById: async () => null, releaseLease: async () => ({}) as never, }, environmentRuntime: { // The runtime records the provider-attested expiry. Here the provider // grants an expiry at or before the requested deadline. acquireRunLease: async (input: { requestedExpiresAt?: Date | null }) => { forwardedExpiresAt = input.requestedExpiresAt; const bounded = new Date(Math.min(deadline, deadline + 5_000)); return { environment: { id: "env-1", driver: "sandbox" }, lease: { id: "lease-live", expiresAt: bounded }, leaseContext: {}, } as never; }, getDriver: () => ({ releaseRunLease }) as never, }, openLivePtySession: async () => openPtySession, }); const acquired = await provider.acquire({ scope: SCOPE, deadline }); // The provider forwarded the session deadline to the runtime acquire. expect(forwardedExpiresAt).toBeInstanceOf(Date); expect((forwardedExpiresAt as Date).getTime()).toBe(deadline); expect(acquired.leaseId).toBe("lease-live"); // The lease held no expiry after the deadline. expect(releaseRunLease).not.toHaveBeenCalled(); }); it("releases the remote lease and fails closed when the acquired expiry is absent, invalid, or later", async () => { const deadline = Date.now() + 60_000; const cases: Array<{ label: string; expiresAt: unknown }> = [ { label: "absent", expiresAt: null }, { label: "invalid", expiresAt: new Date("not-a-date") }, { label: "later", expiresAt: new Date(deadline + 1) }, ]; for (const testCase of cases) { const releaseRunLease = vi.fn(async () => null); const openLivePtySession = vi.fn(async () => { throw new Error("must not open a pty for an unbounded lease"); }); const provider = createProductionSetupTokenSandboxProvider({ environments: { getById: async () => ({ id: "env-1", name: "Sandbox", driver: "sandbox", status: "active" }) as never, getLeaseById: async () => null, releaseLease: async () => ({}) as never, }, environmentRuntime: { acquireRunLease: async () => ({ environment: { id: "env-1", driver: "sandbox" }, lease: { id: "lease-unbounded", expiresAt: testCase.expiresAt }, leaseContext: {}, }) as never, getDriver: () => ({ releaseRunLease }) as never, }, openLivePtySession, }); await expect(provider.acquire({ scope: SCOPE, deadline })).rejects.toMatchObject({ status: 503, }); // The provider released the acquired remote lease through the driver. expect(releaseRunLease, testCase.label).toHaveBeenCalledTimes(1); // The provider failed closed before it opened the login pseudo-terminal. expect(openLivePtySession, testCase.label).not.toHaveBeenCalled(); } }); it("runs the provider driver release by id for a fresh instance with an empty lease map", async () => { // A fresh provider instance holds no in-memory lease record, as after a // restart. The release must resolve the stored lease and its environment and // run the provider driver release, not only the database row write. const releaseRunLease = vi.fn(async () => null); const releaseLease = vi.fn(async () => ({}) as never); const getLeaseById = vi.fn(async () => ({ id: "lease-restart", environmentId: "env-1" }) as never); const getById = vi.fn(async () => ({ id: "env-1", driver: "sandbox", status: "active" }) as never); const provider = createProductionSetupTokenSandboxProvider({ environments: { getById, getLeaseById, releaseLease }, environmentRuntime: { acquireRunLease: vi.fn(), getDriver: () => ({ releaseRunLease }) as never, }, openLivePtySession: async () => { throw new Error("must not open a pty for a restart release"); }, }); await provider.release("lease-restart"); // The provider resolved the lease by id and released through the driver. expect(getLeaseById).toHaveBeenCalledWith("lease-restart"); expect(releaseRunLease).toHaveBeenCalledTimes(1); // The provider tore down the remote sandbox through the driver, not only the // database lease row. expect(releaseLease).not.toHaveBeenCalled(); }); }); describe("worker-bound live pseudo-terminal opener", () => { it("resolves the lease to its provider lease id and drives the worker route gate", async () => { const session = { onData: () => {}, write: () => {}, wait: async () => ({ exitCode: 0 }), kill: () => {}, close: async () => {}, }; const openLoginPtySession = vi.fn( async (_pluginId: string, _input: Record) => session, ); const getLeaseById = vi.fn(async () => ({ providerLeaseId: "provider-lease-9", metadata: { pluginId: "paperclip.daytona", provider: "daytona" }, })); const openLivePtySession = createWorkerBoundLoginPtyOpener({ workerManager: { openLoginPtySession }, environments: { getLeaseById }, }); const opener = await openLivePtySession({ scope: SCOPE, environmentId: "env-1", leaseId: "lease-42", }); // The opener resolved the server lease id to the provider lease id. expect(getLeaseById).toHaveBeenCalledWith("lease-42"); // The opener ignores the incoming command argument. It resolves the closed // command key from the trusted adapter type and derives the session home. const opened = await opener("caller-supplied-ignored"); // The opener drove the manager route gate with the resolved worker target, the // closed command key, and a validated session home. The manager mints the host // route id, so the opener passes none. The open carries no command string. expect(openLoginPtySession).toHaveBeenCalledTimes(1); const [pluginId, openInput] = openLoginPtySession.mock.calls[0]; expect(pluginId).toBe("paperclip.daytona"); expect(openInput).toMatchObject({ driverKey: "daytona", companyId: "company-1", environmentId: "env-1", providerLeaseId: "provider-lease-9", loginCommandKey: "claude", }); expect(openInput).not.toHaveProperty("command"); // The session home is the fixed root, one slash, and one UUID. expect(openInput.sessionHome).toMatch( /^\/tmp\/paperclip-adapter-login\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/, ); expect(opened).toBe(session); }); it("fails closed when the adapter type has no login command key", async () => { const openLoginPtySession = vi.fn(async () => ({ onData: () => {}, write: () => {}, wait: async () => ({ exitCode: 0 }), kill: () => {}, close: async () => {}, })); const openLivePtySession = createWorkerBoundLoginPtyOpener({ workerManager: { openLoginPtySession }, environments: { getLeaseById: async () => ({ providerLeaseId: "provider-lease-9", metadata: { pluginId: "paperclip.daytona", provider: "daytona" }, }), }, }); // An unmapped adapter type confers no command authority. The opener fails // closed before it reaches the worker manager. await expect( openLivePtySession({ scope: { ...SCOPE, adapterType: "gemini_local" }, environmentId: "env-1", leaseId: "lease-42", }), ).rejects.toMatchObject({ status: 503 }); expect(openLoginPtySession).not.toHaveBeenCalled(); }); it("fails closed when the lease carries no sandbox worker binding", async () => { const openLoginPtySession = vi.fn(); const openLivePtySession = createWorkerBoundLoginPtyOpener({ workerManager: { openLoginPtySession }, // The lease resolves but carries no provider lease id or plugin id. environments: { getLeaseById: async () => ({ providerLeaseId: null, metadata: {} }), }, }); await expect( openLivePtySession({ scope: SCOPE, environmentId: "env-1", leaseId: "lease-42" }), ).rejects.toMatchObject({ status: 503 }); // The opener never reached the worker manager. expect(openLoginPtySession).not.toHaveBeenCalled(); }); it("fails closed when the lease is missing", async () => { const openLivePtySession = createWorkerBoundLoginPtyOpener({ workerManager: { openLoginPtySession: vi.fn() }, environments: { getLeaseById: async () => null }, }); await expect( openLivePtySession({ scope: SCOPE, environmentId: "env-1", leaseId: "missing" }), ).rejects.toMatchObject({ status: 503 }); }); }); // The fixed, closed set of diagnostic lines the login runner emits. The runner // is the sole producer. Every line is an approved non-secret literal. The // application binding forwards each line verbatim, so a captured line must be a // member of this set. const DIAGNOSTIC_ALLOWLIST = new Set([ "[paperclip] Setup-token login: the process stop step errored.", "[paperclip] Setup-token login: the driver dispose step errored.", "[paperclip] Setup-token login: the code input step errored.", "[paperclip] Setup-token login: sent the browser code to the prompt.", "[paperclip] Setup-token login: delivered the credential to the sink.", "[paperclip] Setup-token login: the credential delivery step errored.", "[paperclip] Setup-token login cancelled before start.", "[paperclip] Setup-token login timed out; stopping the process.", "[paperclip] Setup-token login cancelled; stopping the process.", "[paperclip] Setup-token login command ended with a non-zero exit code.", "[paperclip] Setup-token login: the credential did not land; treating the run as a failure.", "[paperclip] Setup-token login: surfaced the sign-in prompt.", "[paperclip] Setup-token login command ended successfully.", ]); // Synthetic sentinels. No real secret is present. The tests assert that no // captured diagnostic line carries any of these values, so the binding never // leaks the token, the browser code, or the authorization URL to a log. const TOKEN_SENTINEL = "sk-ant-oat01-SENTINELTOKENAAAAAAAAAAAA"; const BROWSER_CODE_SENTINEL = "SENTINEL-BROWSER-CODE"; const URL_CLIENT_SENTINEL = "sentinelurlclient"; // Each query value satisfies the shape contract the parser enforces: a 43-to-128 // character `code_challenge`, a 16-to-256 character `state`, and the one pinned // `redirect_uri`. The `client_id` stays the sentinel, so the leak checks still // prove the client id never reaches a log line. const AUTHORIZATION_URL_SENTINEL = "https://claude.com/cai/oauth/authorize" + `?client_id=${URL_CLIENT_SENTINEL}` + "&code=redacted" + "&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM" + "&code_challenge_method=S256" + "&redirect_uri=https%3A%2F%2Fplatform.claude.com%2Foauth%2Fcode%2Fcallback" + "&response_type=code" + "&scope=user%3Ainference" + "&state=Xy7Kd2Pq9Rn4Vb8Lf1Mw6Zc3Hj0Tg5Us"; // A complete sign-in prompt block. The parser binds the authorization URL to // the preamble and the prompt line after it. const PROMPT_OUTPUT = [ "Browser didn’t open? Use the url below to sign in (c to copy)", AUTHORIZATION_URL_SENTINEL, "Paste code here if prompted >", ].join("\n"); // A complete success record. The parser binds the token between the two // anchors. const CREDENTIAL_OUTPUT = [ "✓ Long-lived authentication token created successfully!", "", SETUP_TOKEN_BEFORE_ANCHOR, "", TOKEN_SENTINEL, "", SETUP_TOKEN_AFTER_ANCHOR, ].join("\n"); /** * A fake sandbox that streams controllable output. The test emits the prompt * block and the credential block on demand, and it ends the login process with * a chosen exit code. The runner drives its full diagnostic path over this * fake, so the test captures every line the binding forwards. */ function createStreamingSandbox() { let listener: ((chunk: string) => void) | null = null; let resolveExit: (value: { exitCode: number | null }) => void = () => {}; const exit = new Promise<{ exitCode: number | null }>((resolve) => { resolveExit = resolve; }); const writes: string[] = []; const openPtySession: LoginPtySessionOpener = async () => ({ onData(next): void { listener = next; }, write(data): void { writes.push(data); }, wait: () => exit, kill(): void { resolveExit({ exitCode: 137 }); }, async close(): Promise {}, }); const provider: SetupTokenSandboxProvider = { async acquire() { return { leaseId: "lease-1", openPtySession }; }, async release() {}, }; return { provider, emit(chunk: string): void { listener?.(chunk); }, finishProcess(exitCode: number | null): void { resolveExit({ exitCode }); }, get writes() { return writes; }, }; } /** Waits `ms` milliseconds of real time, so the code-submit settle can pass. */ function delay(ms: number): Promise { return new Promise((resolve) => setTimeout(resolve, ms)); } /** * Drives one login run through the binding factory and returns every captured * diagnostic line. The caller controls the run through `drive`, so one call * exercises the success path and another exercises the failure path. The `drive` * callback receives the sandbox and the process handle. */ async function captureDiagnostics( drive: ( sandbox: ReturnType, process: SetupTokenLoginProcess, ) => Promise, ): Promise { const captured: string[] = []; const sandbox = createStreamingSandbox(); const store = createRecordingStore(); const transport = buildSetupTokenLoginTransport({ sandbox: sandbox.provider, store: store.store, log: (line) => captured.push(line), }); await transport.leases.acquire({ scope: SCOPE, deadline: Date.now() + 60_000 }); const controller = new AbortController(); const process = transport.factory({ scope: SCOPE, onPrompt: () => {}, onCredential: async () => {}, timeoutMs: 60_000, signal: controller.signal, }); // Let the transport open the session and register the output listener before // the driver emits any output. await flush(); await drive(sandbox, process); await process.done; await flush(); return captured; } /** Asserts a captured diagnostic set is allowlisted and carries no secret. */ function expectSafeDiagnostics(captured: string[]): void { for (const line of captured) { expect(DIAGNOSTIC_ALLOWLIST.has(line), line).toBe(true); } const joined = captured.join("\n"); expect(joined).not.toContain(TOKEN_SENTINEL); expect(joined).not.toContain(BROWSER_CODE_SENTINEL); expect(joined).not.toContain(AUTHORIZATION_URL_SENTINEL); expect(joined).not.toContain(URL_CLIENT_SENTINEL); } describe("setup-token production transport binding diagnostics", () => { it("forwards only allowlisted, non-secret diagnostics on a success path", async () => { const captured = await captureDiagnostics(async (sandbox, process) => { // Surface the prompt. The runner logs the prompt line and starts the code // input step, which waits for the browser code. sandbox.emit(PROMPT_OUTPUT); await flush(); // Submit the browser code. The runner writes the code, lets the paste // buffer settle, writes the Enter byte, and logs the code-sent line. process.submitCode(BROWSER_CODE_SENTINEL); await delay(250); // Deliver the token, then end the process cleanly. The runner binds the // token, delivers it to the sink, and logs the success line. sandbox.emit(CREDENTIAL_OUTPUT); sandbox.finishProcess(0); }); // The run reached the success lines, so the capture is not empty. expect(captured).toContain("[paperclip] Setup-token login: surfaced the sign-in prompt."); expect(captured).toContain( "[paperclip] Setup-token login: delivered the credential to the sink.", ); expect(captured).toContain("[paperclip] Setup-token login command ended successfully."); // The runner sent the browser code, so the code-input path ran. The code // still never reaches a log line. expect(captured).toContain("[paperclip] Setup-token login: sent the browser code to the prompt."); expectSafeDiagnostics(captured); }); it("forwards only allowlisted, non-secret diagnostics on a failure path", async () => { const captured = await captureDiagnostics(async (sandbox, process) => { void process; // The login process ends with a non-zero exit code before any prompt. sandbox.finishProcess(1); }); expect(captured).toContain( "[paperclip] Setup-token login command ended with a non-zero exit code.", ); expectSafeDiagnostics(captured); }); }); // A compile-time guard: a terminal state helper stays importable for the tests. void isTerminalSessionState; // A synthetic token. No real token is present. The tests assert the token never // reaches a log or an error message. const SYNTH_TOKEN = "sk-ant-oat01-SYNTHETICSYNTHETICSYNTHETIC01"; describe("buildClaudeOAuthWriteInput", () => { it("maps a scope with no overwrite to a first_write input", () => { const input = buildClaudeOAuthWriteInput(SCOPE, "session-7", SYNTH_TOKEN); expect(input).toEqual({ sessionId: "session-7", mode: "first_write", value: SYNTH_TOKEN }); }); it("maps a scope with the overwrite capture to a confirmed_rotation input", () => { const overwriteScope: SetupTokenSessionScope = { ...SCOPE, confirmedOverwrite: { expectedSecretId: "secret-9", expectedLatestVersion: 4 }, }; const input = buildClaudeOAuthWriteInput(overwriteScope, "session-8", SYNTH_TOKEN); expect(input).toEqual({ sessionId: "session-8", mode: "confirmed_rotation", value: SYNTH_TOKEN, expectedSecretId: "secret-9", expectedLatestVersion: 4, }); }); }); describe("createSetupTokenSecretWriter wiring", () => { it("carries the writer through the transport binding to the router", () => { const sandbox = createFakeSandbox(); const store = createRecordingStore(); // The writer never runs here; a stub db is enough to assert the binding // forwards the writer reference to the router. const writer = createSetupTokenSecretWriter({ db: {} as never }); const transport = buildSetupTokenLoginTransport({ sandbox: sandbox.provider, store: store.store, completeCredential: writer, }); // The binding forwards the writer, so the router uses the real atomic write // instead of the deferred fail-closed default. expect(transport.completeCredential).toBe(writer); }); });