test(chaos): allow only the node executable through the oracle sandbox; accept table-driven R1 tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
DottaandClaude Opus 5.5 committed 2026-11-08 15:54:17 -06:00
1 parent 55d8762258
commit d40a749409
5 files changed
+16 -12

No files matched your search

+3
View File
@@ -243,6 +243,9 @@ describe("T-oracles", () => {
slug: { exitCode: 0, stdout: "[\"hello-chaos-world\",\"-r1-paperclip-chaos-2026-\"]\n", stderr: "" } } }).verdict).toBe("fail"); slug: { exitCode: 0, stdout: "[\"hello-chaos-world\",\"-r1-paperclip-chaos-2026-\"]\n", stderr: "" } } }).verdict).toBe("fail");
expect(r1TestChecksSlug("import test from \"node:test\";")).toBe(false); expect(r1TestChecksSlug("import test from \"node:test\";")).toBe(false);
expect(r1TestChecksSlug("import test from \"node:test\"; import assert from \"node:assert\"; assert.ok(chaosSlug(\"x\"));")).toBe(false); expect(r1TestChecksSlug("import test from \"node:test\"; import assert from \"node:assert\"; assert.ok(chaosSlug(\"x\"));")).toBe(false);
expect(r1TestChecksSlug([ "import test from \"node:test\";", "import assert from \"node:assert/strict\";",
"for (const [input, want] of [[\"Hello Chaos World\", \"hello-chaos-world\"], [\"a b\", \"a-b\"], [\"-x-\", \"x\"]])",
" test(input, () => assert.equal(chaosSlug(input), want));"].join("\n"))).toBe(true);
const argv = workloadDefinitions.R1.collect.commands.find(command => command.name === "node_test")!.argv; const argv = workloadDefinitions.R1.collect.commands.find(command => command.name === "node_test")!.argv;
expect(typeof argv === "function" ? argv("abcd1234") : argv).toEqual([process.execPath, "--test", "packages/shared/src/chaos/slug-abcd1234.test.ts"]); expect(typeof argv === "function" ? argv("abcd1234") : argv).toEqual([process.execPath, "--test", "packages/shared/src/chaos/slug-abcd1234.test.ts"]);
}); });
+2 -2
View File
@@ -1,6 +1,6 @@
import { spawnSync } from "node:child_process"; import { spawnSync } from "node:child_process";
import { randomUUID } from "node:crypto"; import { randomUUID } from "node:crypto";
import { readFile, readdir, realpath } from "node:fs/promises"; import { readFile, readdir } from "node:fs/promises";
import os from "node:os"; import os from "node:os";
import path from "node:path"; import path from "node:path";
import { setTimeout as delay } from "node:timers/promises"; import { setTimeout as delay } from "node:timers/promises";
@@ -761,7 +761,7 @@ export class TaskObserver {
for (const command of definition.collect.commands) { for (const command of definition.collect.commands) {
const argv = typeof command.argv === "function" ? command.argv(this.plan.nonce) : command.argv; const argv = typeof command.argv === "function" ? command.argv(this.plan.nonce) : command.argv;
commands[command.name] = await runOracleCommand(argv, workspacePath, { protectedRoots: this.protectedRoots, commands[command.name] = await runOracleCommand(argv, workspacePath, { protectedRoots: this.protectedRoots,
readableRoots: argv[0] === process.execPath ? [path.dirname(path.dirname(await realpath(process.execPath)))] : [] }); readableFiles: argv[0] === process.execPath ? [process.execPath] : [] });
} }
} }
return { return {
+5 -4
View File
@@ -17,10 +17,11 @@ describe("oracle sandbox", () => {
expect(profile.indexOf('(allow file-read* (subpath "/w")')).toBeGreaterThan(profile.indexOf("(deny file-read*")); expect(profile.indexOf('(allow file-read* (subpath "/w")')).toBeGreaterThan(profile.indexOf("(deny file-read*"));
}); });
it("re-allows only the named readable roots, such as the node install, under a protected root", () => { it("re-allows only the named files, such as the node executable, never a directory under a protected root", () => {
const profile = oracleSandboxProfile({ workspace: "/w", scratch: "/s", protectedRoots: ["/Users/me"], readableRoots: ["/Users/me/.nvm/versions/node/v26"] }); const profile = oracleSandboxProfile({ workspace: "/w", scratch: "/s", protectedRoots: ["/Users/me"], readableFiles: ["/Users/me/bin/node"] });
expect(profile.indexOf('(allow file-read* (subpath "/Users/me/.nvm/versions/node/v26"))')).toBeGreaterThan(profile.indexOf('(deny file-read* (subpath "/Users/me"))')); expect(profile.indexOf('(allow file-read* (literal "/Users/me/bin/node"))')).toBeGreaterThan(profile.indexOf('(deny file-read* (subpath "/Users/me"))'));
expect(oracleSandboxProfile({ workspace: "/w", scratch: "/s", protectedRoots: ["/Users/me"] })).not.toContain(".nvm"); expect(profile).not.toContain('(allow file-read* (subpath "/Users/me")');
expect(profile).not.toContain('(allow file-read* (subpath "/Users/me/bin")');
}); });
it("refuses to run unsandboxed on other platforms unless allowed", async () => { it("refuses to run unsandboxed on other platforms unless allowed", async () => {
+4 -4
View File
@@ -18,13 +18,13 @@ function sbplString(value: string) { return `"${value.replaceAll("\\", "\\\\").r
* example ~/.secrets or ~/.ssh), write anywhere except a scratch directory, or * example ~/.secrets or ~/.ssh), write anywhere except a scratch directory, or
* use the network. The workspace stays readable. * use the network. The workspace stays readable.
*/ */
export function oracleSandboxProfile(input: { workspace: string; scratch: string; protectedRoots: readonly string[]; readableRoots?: readonly string[] }): string { export function oracleSandboxProfile(input: { workspace: string; scratch: string; protectedRoots: readonly string[]; readableFiles?: readonly string[] }): string {
const lines = ["(version 1)", "(allow default)", "(deny network*)", "(deny file-write*)", const lines = ["(version 1)", "(allow default)", "(deny network*)", "(deny file-write*)",
`(allow file-write* (subpath ${sbplString(input.scratch)}) (literal "/dev/null") (literal "/dev/tty"))`]; `(allow file-write* (subpath ${sbplString(input.scratch)}) (literal "/dev/null") (literal "/dev/tty"))`];
for (const root of input.protectedRoots) lines.push(`(deny file-read* (subpath ${sbplString(root)}))`); for (const root of input.protectedRoots) lines.push(`(deny file-read* (subpath ${sbplString(root)}))`);
// Later rules win: re-allow the workspace and scratch even if they sit under a protected root. // Later rules win: re-allow the workspace and scratch even if they sit under a protected root.
lines.push(`(allow file-read* (subpath ${sbplString(input.workspace)}) (subpath ${sbplString(input.scratch)}))`); lines.push(`(allow file-read* (subpath ${sbplString(input.workspace)}) (subpath ${sbplString(input.scratch)}))`);
for (const root of input.readableRoots ?? []) lines.push(`(allow file-read* (subpath ${sbplString(root)}))`); for (const file of input.readableFiles ?? []) lines.push(`(allow file-read* (literal ${sbplString(file)}))`);
return lines.join("\n"); return lines.join("\n");
} }
@@ -35,7 +35,7 @@ export function oracleSandboxProfile(input: { workspace: string; scratch: string
* temporary HOME alone does not stop absolute-path reads. * temporary HOME alone does not stop absolute-path reads.
*/ */
export async function runOracleCommand(argv: readonly string[], workspace: string, options: { export async function runOracleCommand(argv: readonly string[], workspace: string, options: {
protectedRoots?: readonly string[]; readableRoots?: readonly string[]; timeoutMs?: number; platform?: NodeJS.Platform; allowUnsandboxed?: boolean; protectedRoots?: readonly string[]; readableFiles?: readonly string[]; timeoutMs?: number; platform?: NodeJS.Platform; allowUnsandboxed?: boolean;
} = {}): Promise<OracleCommandResult> { } = {}): Promise<OracleCommandResult> {
const platform = options.platform ?? process.platform; const platform = options.platform ?? process.platform;
const sandboxed = platform === "darwin"; const sandboxed = platform === "darwin";
@@ -51,7 +51,7 @@ export async function runOracleCommand(argv: readonly string[], workspace: strin
const [command, args] = sandboxed const [command, args] = sandboxed
? ["/usr/bin/sandbox-exec", ["-p", oracleSandboxProfile({ workspace: await resolve(workspace), scratch, ? ["/usr/bin/sandbox-exec", ["-p", oracleSandboxProfile({ workspace: await resolve(workspace), scratch,
protectedRoots: await Promise.all((options.protectedRoots ?? [os.homedir()]).map(resolve)), protectedRoots: await Promise.all((options.protectedRoots ?? [os.homedir()]).map(resolve)),
readableRoots: await Promise.all((options.readableRoots ?? []).map(resolve)) }), ...argv]] readableFiles: await Promise.all((options.readableFiles ?? []).map(resolve)) }), ...argv]]
: [argv[0]!, argv.slice(1)]; : [argv[0]!, argv.slice(1)];
try { try {
const { stdout, stderr } = await execFileAsync(command, args, { cwd: workspace, env, timeout: options.timeoutMs ?? 60_000, maxBuffer: 1024 * 1024 }); const { stdout, stderr } = await execFileAsync(command, args, { cwd: workspace, env, timeout: options.timeoutMs ?? 60_000, maxBuffer: 1024 * 1024 });
+2 -2
View File
@@ -43,8 +43,8 @@ const r1Test = (nonce: string) => `packages/shared/src/chaos/slug-${nonce}.test.
export function r1TestChecksSlug(text: string | null | undefined): boolean { export function r1TestChecksSlug(text: string | null | undefined): boolean {
if (!text) return false; if (!text) return false;
const assertions = text.match(/\bassert(\.\w+)?\s*\(/g)?.length ?? 0; return text.includes("node:test") && text.includes("node:assert") && /\bassert(\.\w+)?\s*\(/.test(text)
return text.includes("node:test") && text.includes("node:assert") && text.includes("chaosSlug(") && assertions >= 3; && text.includes("chaosSlug") && text.includes("hello-chaos-world");
} }
/** bench.sh must have actually run: at least this long between start and finish. */ /** bench.sh must have actually run: at least this long between start and finish. */
export const BENCH_MIN_SECONDS = 200; export const BENCH_MIN_SECONDS = 200;