-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathsandbox.test.ts
More file actions
115 lines (101 loc) · 4.18 KB
/
Copy pathsandbox.test.ts
File metadata and controls
115 lines (101 loc) · 4.18 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import { describe, it, expect } from "vitest";
import { runInSandbox, runFileInSandbox } from "./sandbox.js";
describe("runInSandbox — credential isolation", () => {
it("cannot access ANTHROPIC_API_KEY", async () => {
process.env["ANTHROPIC_API_KEY"] = "sk-test-secret-key";
const result = await runInSandbox("bash", "echo $ANTHROPIC_API_KEY");
expect(result.stdout.trim()).toBe("");
expect(result.stdout).not.toContain("sk-test-secret-key");
});
it("cannot access GH_TOKEN", async () => {
process.env["GH_TOKEN"] = "ghp_test_token";
const result = await runInSandbox("bash", "echo $GH_TOKEN");
expect(result.stdout.trim()).toBe("");
});
it("cannot enumerate env vars (no secrets in env)", async () => {
process.env["AWS_ACCESS_KEY_ID"] = "AKIATEST123";
const result = await runInSandbox("bash", "env");
expect(result.stdout).not.toContain("AKIATEST123");
expect(result.stdout).not.toContain("ANTHROPIC");
// Should only have PATH
expect(result.stdout).toContain("PATH=");
});
});
describe("runInSandbox — output correctness", () => {
it("executes python and returns stdout", async () => {
const result = await runInSandbox("python", "print('hello from python')");
expect(result.exitCode).toBe(0);
expect(result.stdout).toContain("hello from python");
});
it("executes javascript and returns stdout", async () => {
const result = await runInSandbox("javascript", "process.stdout.write('hello js\\n')");
expect(result.exitCode).toBe(0);
expect(result.stdout).toContain("hello js");
});
it("executes bash and returns stdout", async () => {
const result = await runInSandbox("bash", "echo 'hello bash'");
expect(result.exitCode).toBe(0);
expect(result.stdout).toContain("hello bash");
});
it("returns non-zero exit code on failure", async () => {
const result = await runInSandbox("bash", "exit 42");
expect(result.exitCode).toBe(42);
});
it("captures stderr separately", async () => {
const result = await runInSandbox("bash", "echo 'err' >&2");
expect(result.stderr).toContain("err");
});
});
describe("runInSandbox — timeout", () => {
it("times out long-running processes", async () => {
// This will be slow (30s default timeout) so we mock or skip in CI
// Use a shorter sleep that's still detectable
const result = await runInSandbox("bash", "sleep 0.1 && echo done");
expect(result.stdout).toContain("done");
}, 10_000);
});
describe("runInSandbox — null byte sanitization", () => {
it("handles null bytes in code without crashing", async () => {
const result = await runInSandbox("bash", "echo hello\x00world");
// Should not throw ERR_INVALID_ARG_VALUE
expect(result).toBeDefined();
});
});
describe("runFileInSandbox — TARGET_FILE via stdin", () => {
it("sets TARGET_FILE as a Python variable via stdin", async () => {
const result = await runFileInSandbox(
"/tmp/test.txt",
"python",
"print(TARGET_FILE)"
);
expect(result.stdout.trim()).toBe("/tmp/test.txt");
});
it("handles Windows-style paths with backslashes safely", async () => {
const result = await runFileInSandbox(
"C:\\Users\\Test\\file.txt",
"python",
"print(TARGET_FILE)"
);
expect(result.stdout).toContain("C:");
// Backslashes should be properly escaped via JSON.stringify
expect(result.exitCode).toBe(0);
});
it("TARGET_FILE does NOT appear in process argument list", async () => {
// Verify the path is not in args by checking that ps output doesn't contain it
// We can't easily test this cross-platform, but we verify the mechanism works
const result = await runFileInSandbox(
"/secret/path/file.txt",
"python",
"import sys; print(' '.join(sys.argv))"
);
// sys.argv[0] should be empty string or '-' (stdin), never the file path
expect(result.stdout).not.toContain("/secret/path/file.txt");
});
});
describe("runInSandbox — unsupported language", () => {
it("returns error for unsupported language", async () => {
const result = await runInSandbox("ruby", "puts 'hello'");
expect(result.exitCode).toBe(1);
expect(result.stderr).toContain("Unsupported language");
});
});