import { afterEach, describe, expect, it } from "bun:test"; import { mkdtemp, rm } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import type { Request } from "express"; import { attachTrustedAgentContext, getLocalAgentContext, ProductContextError, getTrustedProductCredential, isTrustedTaskDispatchRequest, } from "../../src/context/localContext.js"; import { SessionMetadataStore } from "../../src/sessions/metadataStore.js"; const originalFetch = globalThis.fetch; afterEach(() => { globalThis.fetch = originalFetch; }); const requestWithHeaders = (headers: Record) => ({ header: (name: string) => headers[name.toLowerCase()], }) as Request; const authResponse = (userId: string, projectId: string) => ({ is_superuser: false, keycloak_sub: `sub-${userId}`, network: "supply", permissions: ["analysis:read"], project_id: projectId, project_role: "member", role: "user", token_expires_at: "2026-09-17T00:00:00Z", user_id: userId, username: userId, }); describe("trusted product chat context", () => { it("authorizes message dispatch only for verified service accounts with execution permission", async () => { const request = requestWithHeaders({ authorization: "Bearer verified", "x-project-id": "project-a" }); for (const [role, permissions, allowed] of [ ["user", ["operational_task.execute"], false], ["service", ["operational_task.view"], false], ["service", ["operational_task.execute"], true], ["user", [], false], ] as const) { globalThis.fetch = (async () => Response.json({ ...authResponse("actor", "project-a"), role, permissions })) as unknown as typeof fetch; await attachTrustedAgentContext(request); expect(isTrustedTaskDispatchRequest(request)).toBe(allowed); } }); it("verifies personal chats by Bearer without assigning a shared project or business credential", async () => { const requests: Array<{ url: string; headers: Headers }> = []; globalThis.fetch = (async (input: RequestInfo | URL, init?: RequestInit) => { requests.push({ url: String(input), headers: new Headers(init?.headers) }); return Response.json({ user_id: requests.length === 1 ? "user-a" : "user-b" }); }) as unknown as typeof fetch; const first = requestWithHeaders({ authorization: "Bearer personal-a", "x-agent-local-user": "spoofed" }); const second = requestWithHeaders({ authorization: "Bearer personal-b" }); await attachTrustedAgentContext(first); await attachTrustedAgentContext(second); expect(requests[0]!.url).toEndWith("/api/v1/access-context"); expect(requests[0]!.headers.has("X-Project-Id")).toBeFalse(); expect(getLocalAgentContext(first).userId).toBe("user-a"); expect(getLocalAgentContext(first).projectId).toBeUndefined(); expect(getLocalAgentContext(first).projectKey).not.toBe(getLocalAgentContext(second).projectKey); expect(getTrustedProductCredential(first)).toBeNull(); }); it("does not turn a project header or expired personal token into anonymous access", async () => { globalThis.fetch = (async () => Response.json({ detail: "expired" }, { status: 401 })) as unknown as typeof fetch; await expect(attachTrustedAgentContext(requestWithHeaders({ "x-project-id": "project-a" }))).rejects.toBeInstanceOf(ProductContextError); await expect(attachTrustedAgentContext(requestWithHeaders({ authorization: "Bearer expired-personal" }))).rejects.toBeInstanceOf(ProductContextError); }); it("uses verified product users and projects instead of spoofable local headers", async () => { const identities = [authResponse("product-user-1", "project-1"), authResponse("product-user-2", "project-2")]; globalThis.fetch = (async () => Response.json(identities.shift())) as unknown as typeof fetch; const first = requestWithHeaders({ authorization: "Bearer product-token-1", "x-project-id": "project-1", "x-agent-local-user": "spoofed-user", "x-agent-local-project": "spoofed-project", }); const second = requestWithHeaders({ authorization: "Bearer product-token-2", "x-project-id": "project-2", "x-agent-local-user": "spoofed-user", "x-agent-local-project": "spoofed-project", }); await attachTrustedAgentContext(first); await attachTrustedAgentContext(second); const firstContext = getLocalAgentContext(first); const secondContext = getLocalAgentContext(second); expect(firstContext.userId).toBe("product-user-1"); expect(firstContext.projectId).toBe("project-1"); expect(secondContext.userId).toBe("product-user-2"); expect(secondContext.projectId).toBe("project-2"); expect(firstContext.actorKey).not.toBe(secondContext.actorKey); expect(firstContext.projectKey).not.toBe(secondContext.projectKey); const localSpoof = requestWithHeaders({ "x-agent-local-user": "product-user-1", "x-agent-local-project": "project-1", }); await attachTrustedAgentContext(localSpoof); const localContext = getLocalAgentContext(localSpoof); expect(localContext.actorKey).not.toBe(firstContext.actorKey); expect(localContext.projectKey).not.toBe(firstContext.projectKey); const tempDir = await mkdtemp(join(tmpdir(), "tjwater-product-session-")); try { const store = new SessionMetadataStore(tempDir); await store.initialize(); await store.ensure({ ...firstContext, sessionId: "private-session" }); expect(await store.get(secondContext, "private-session")).toBeNull(); expect((await store.get(firstContext, "private-session"))?.sessionId).toBe("private-session"); } finally { await rm(tempDir, { force: true, recursive: true }); } }); it("fails closed when any supplied product credential cannot be verified", async () => { globalThis.fetch = (async () => Response.json({ detail: "expired" }, { status: 401 })) as unknown as typeof fetch; const request = requestWithHeaders({ authorization: "Bearer expired-token", "x-project-id": "project-1", }); expect(attachTrustedAgentContext(request)).rejects.toBeInstanceOf(ProductContextError); }); });