import { afterEach, describe, expect, it } from "bun:test"; import express from "express"; import { type Server } from "node:http"; import { registerExternalInformationBridge } from "../../src/routes/externalInformationBridge.js"; import { ExternalInformationClient } from "../../src/product/external-information-client.js"; import { bindSessionCredential, releaseSessionCredential } from "../../src/product/credential-refresh.js"; import { removeRuntimeSessionContext, setRuntimeSessionContext } from "../../src/runtime/sessionContext.js"; let agent: Server | undefined; let backend: Server | undefined; const session = "external-information-bridge-test"; afterEach(async () => { for (const server of [agent, backend]) if (server) await new Promise((resolve) => server.close(() => resolve())); releaseSessionCredential(session); removeRuntimeSessionContext(session); }); describe("external information bridge", () => { it("calls search and geocode with the owning session and rejects browser, scope and URL injection", async () => { let calls = 0; const product = express(); product.use(express.json()); product.post(["/api/v1/web-searches", "/api/v1/geocoding-requests"], (req, res) => { calls++; expect(req.header("Authorization")).toBe("Bearer TEST_ONLY"); expect(req.header("X-Project-Id")).toBe("trusted-project"); res.json(req.path.endsWith("web-searches") ? { data: { results: [req.body.query] } } : { status: "0", location: { lon: "116.4", lat: "39.9" } }); }); backend = product.listen(0, "127.0.0.1"); await new Promise((resolve) => backend!.once("listening", resolve)); const app = express(); app.use(express.json()); registerExternalInformationBridge(app, { internalToken: "TEST_ONLY", client: new ExternalInformationClient(`http://127.0.0.1:${(backend.address() as { port: number }).port}`) }); agent = app.listen(0, "127.0.0.1"); await new Promise((resolve) => agent!.once("listening", resolve)); const base = `http://127.0.0.1:${(agent.address() as { port: number }).port}`; const post = (name: string, body: unknown, headers: Record = {}) => fetch(`${base}/internal/tools/${name}`, { method: "POST", headers: { "Content-Type": "application/json", "x-agent-internal-token": "TEST_ONLY", ...headers }, body: JSON.stringify(body) }); setRuntimeSessionContext({ sessionId: session, clientSessionId: session, actorKey: "owner", projectKey: "trusted-project", traceId: "test", purpose: "ordinary_chat" }); bindSessionCredential(session, { authorization: "Bearer TEST_ONLY", projectId: "trusted-project" }); expect((await post("web-search", { session_id: session, input: { query: "政策" } })).status).toBe(200); const coordinates = await post("geocode", { session_id: session, input: { keyword: "北京" } }); expect(await coordinates.json()).toMatchObject({ center: [116.4, 39.9] }); expect((await post("web-search", { session_id: session, input: { query: "test" } }, { Origin: "http://browser" })).status).toBe(403); expect((await post("web-search", { session_id: "missing", input: { query: "test" } })).status).toBe(403); expect((await post("web-search", { session_id: session, input: { query: "test", url: "http://untrusted" } })).status).toBe(400); expect((await post("geocode", { session_id: session, input: { keyword: "北京" }, project_id: "other" })).status).toBe(400); expect(calls).toBe(2); releaseSessionCredential(session); expect((await post("geocode", { session_id: session, input: { keyword: "北京" } })).status).toBe(401); expect(calls).toBe(2); }); });