Files
next-tjwater-agent/tests/routes/externalInformationBridge.test.ts
T
jiang 7383ed6d13 feat(agent): migrate OpenCode 2 runtime and sandbox tools
Upgrade the runtime and native plugin API to OpenCode 2.0.24. Restore
session-scoped learning, credential renewal, typed project queries and
controlled frontend actions from the legacy Agent.

Replace host Shell execution with an approved Landlock/seccomp executor
and persistent per-session workspaces. Add typed Bocha search and Tianditu
geocoding bridges using the owning session credential. Require a working
kernel sandbox before startup and persist workspace files in deployments.

Validation completed before this commit: TypeScript checks, 150 Bun tests,
Docker build and UID 10001 sandbox execution, native Shell approval and
session recovery. Tianditu succeeded; Bocha currently reports exhausted
account quota. Unrelated investigation-report contract work is excluded.
2026-10-08 15:46:37 +08:00

49 lines
3.6 KiB
TypeScript

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<void>((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<void>((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<void>((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<string, string> = {}) => 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);
});
});