src / __tests__ / sandbox.test.ts
import { describe, it, expect } from "vitest";
import { resolve } from "path";
import { tmpdir } from "os";
import { safe, run, SandboxError } from "../sandbox";
// These pin the two guards that keep the shell/file tools from running wild:
// (1) safe() confines every path to the workspace, (2) run() hard-kills on timeout.
const WS = resolve(tmpdir());
describe("safe() — workspace confinement", () => {
it("resolves a relative path inside the workspace", () => {
expect(safe(WS, "sub/file.txt")).toBe(resolve(WS, "sub/file.txt"));
});
it("allows an absolute path that is inside the workspace", () => {
expect(safe(WS, resolve(WS, "a.txt"))).toBe(resolve(WS, "a.txt"));
});
it("rejects an absolute path outside the workspace", () => {
expect(() => safe(WS, "/etc/passwd")).toThrow(SandboxError);
});
it("rejects `..` traversal", () => {
expect(() => safe(WS, "../../../../etc/passwd")).toThrow(SandboxError);
});
it("rejects a sibling dir sharing the workspace prefix", () => {
expect(() => safe(WS, `${WS}-evil/x`)).toThrow(SandboxError);
});
});
describe("run() — execution + timeout", () => {
it("runs a program (no shell) and captures stdout", async () => {
const r = await run("echo", ["hello"], { timeout: 5 });
expect(r.success).toBe(true);
expect(r.stdout).toContain("hello");
}, 8_000);
it("hard-kills a process that exceeds the timeout", async () => {
const start = Date.now();
const r = await run("sleep", ["5"], { timeout: 1 });
expect(r.success).toBe(false);
expect(r.stderr).toMatch(/timeout/i);
expect(Date.now() - start).toBeLessThan(3_000); // killed at ~1s, not 5s
}, 8_000);
});
src / __tests__ / sandbox.test.ts
import { describe, it, expect } from "vitest";
import { resolve } from "path";
import { tmpdir } from "os";
import { safe, run, SandboxError } from "../sandbox";
// These pin the two guards that keep the shell/file tools from running wild:
// (1) safe() confines every path to the workspace, (2) run() hard-kills on timeout.
const WS = resolve(tmpdir());
describe("safe() — workspace confinement", () => {
it("resolves a relative path inside the workspace", () => {
expect(safe(WS, "sub/file.txt")).toBe(resolve(WS, "sub/file.txt"));
});
it("allows an absolute path that is inside the workspace", () => {
expect(safe(WS, resolve(WS, "a.txt"))).toBe(resolve(WS, "a.txt"));
});
it("rejects an absolute path outside the workspace", () => {
expect(() => safe(WS, "/etc/passwd")).toThrow(SandboxError);
});
it("rejects `..` traversal", () => {
expect(() => safe(WS, "../../../../etc/passwd")).toThrow(SandboxError);
});
it("rejects a sibling dir sharing the workspace prefix", () => {
expect(() => safe(WS, `${WS}-evil/x`)).toThrow(SandboxError);
});
});
describe("run() — execution + timeout", () => {
it("runs a program (no shell) and captures stdout", async () => {
const r = await run("echo", ["hello"], { timeout: 5 });
expect(r.success).toBe(true);
expect(r.stdout).toContain("hello");
}, 8_000);
it("hard-kills a process that exceeds the timeout", async () => {
const start = Date.now();
const r = await run("sleep", ["5"], { timeout: 1 });
expect(r.success).toBe(false);
expect(r.stderr).toMatch(/timeout/i);
expect(Date.now() - start).toBeLessThan(3_000); // killed at ~1s, not 5s
}, 8_000);
});