Something went wrong. Try again.
A local-first event pipeline for independent agents, built on Jazz.
Something went wrong. Try again.
3.9 kB · 99 lines
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100import { spawn } from "node:child_process";import fs from "node:fs/promises";import net from "node:net";import os from "node:os";import path from "node:path";import { build } from "esbuild";import { afterEach, describe, expect, test } from "vitest";import { buildSandboxCommand } from "../src/agents/sandbox/launcher.js";import { decodeSingleFrame } from "../src/agents/sandbox/protocol.js";
const temporaryDirectories: string[] = [];const servers: net.Server[] = [];
afterEach(async () => { delete process.env.SANDBOX_CANARY_SECRET; await Promise.all(servers.splice(0).map((server) => new Promise<void>((resolve) => server.close(() => resolve())))); await Promise.all(temporaryDirectories.splice(0).map((directory) => fs.rm(directory, { recursive: true, force: true })));});
describe("disposable model sandbox", () => { test("black-box canary observes only scratch storage, the broker socket, and its own process", async () => { const brokerDirectory = await fs.mkdtemp(path.join(os.tmpdir(), "thoughtstream-sandbox-canary-")); temporaryDirectories.push(brokerDirectory); const canarySocket = path.join(brokerDirectory, "canary.sock"); const server = net.createServer((socket) => { socket.once("data", (chunk) => { if (chunk.toString("utf8") === "ping") socket.end("pong"); else socket.destroy(); }); }); servers.push(server); await new Promise<void>((resolve, reject) => { server.once("error", reject); server.listen(canarySocket, () => resolve()); }); await fs.chmod(canarySocket, 0o666); await fs.chmod(brokerDirectory, 0o755); process.env.SANDBOX_CANARY_SECRET = "must-not-cross-the-boundary";
const canaryBundle = path.join(brokerDirectory, "canary.cjs"); await build({ entryPoints: [path.resolve("test/fixtures/sandbox-canary.ts")], bundle: true, platform: "node", format: "cjs", target: "node22", outfile: canaryBundle, logLevel: "silent", }); const sandbox = buildSandboxCommand(canaryBundle, brokerDirectory, process.execPath); const child = spawn(sandbox.command, sandbox.args, { stdio: ["pipe", "pipe", "pipe"], env: { PATH: "/usr/bin:/bin", LANG: "C" }, detached: true, }); child.stdin.end(); const stdout: Buffer[] = []; const stderr: Buffer[] = []; child.stdout.on("data", (chunk: Buffer) => stdout.push(chunk)); child.stderr.on("data", (chunk: Buffer) => stderr.push(chunk)); const exitCode = await new Promise<number | null>((resolve, reject) => { child.once("error", reject); child.once("exit", resolve); }); expect(exitCode, Buffer.concat(stderr).toString("utf8")).toBe(0); const canary = decodeSingleFrame(Buffer.concat(stdout), 64 * 1024) as Record<string, unknown>; expect(canary).toEqual({ uid: 65_534, gid: 65_534, cwd: "/tmp", environmentKeys: ["HOME", "LANG", "PATH", "THOUGHTSTREAM_BROKER_SOCKET", "TMPDIR"], secretEnvironmentAbsent: true, homeDenied: true, repoDenied: true, etcPasswdDenied: true, hostProcDenied: true, resourceLimits: { "Max address space": "17179869184 17179869184 bytes", "Max core file size": "0 0 bytes", "Max cpu time": "20 20 seconds", "Max file size": "8388608 8388608 bytes", "Max open files": "64 64 files", }, heapLimitBytes: expect.any(Number), runtimeWriteDenied: true, scratchWritten: true, tcpDenied: true, dnsDenied: true, udpDenied: true, brokerUnixSocketAllowed: true, childProcessDenied: true, workerThreadDenied: true, wasiDenied: true, nativeAddonDenied: true, }); expect(canary.heapLimitBytes).toEqual(expect.any(Number)); expect(canary.heapLimitBytes as number).toBeLessThanOrEqual(384 * 1024 * 1024); }, 15_000);});