paperclip/scripts/codex-acp-network-policy.te...

65 lines
3.2 KiB
JavaScript

import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { createRequire } from "node:module";
import { runInNewContext } from "node:vm";
import test from "node:test";
const require = createRequire(new URL("../packages/paperclip-runner/package.json", import.meta.url));
const source = readFileSync(require.resolve("@agentclientprotocol/codex-acp"), "utf8");
const helperStart = source.indexOf("function paperclipSandboxPolicy(");
const helperEnd = source.indexOf("\nvar CodexAcpClient", helperStart);
const methodStart = source.indexOf(" async sendPrompt(");
const methodEnd = source.indexOf("\n async runAgentFileChangeReport", methodStart);
assert.ok(helperStart >= 0 && helperEnd > helperStart, "installed Codex ACP must contain the network policy patch");
assert.ok(methodStart >= 0 && methodEnd > methodStart, "installed Codex ACP must expose the patched turn boundary");
async function turnPolicy(policy, env) {
const context = {
process: { env },
buildPromptItems: (prompt) => prompt,
addAdditionalDirectoriesToSandboxPolicy: (value) => value,
};
const client = runInNewContext(
source.slice(helperStart, helperEnd) + "\n({" + source.slice(methodStart, methodEnd) + "})",
context,
);
client.refreshSkills = async () => {};
client.codexClient = { runTurn: async (params) => params };
return client.sendPrompt(
{ sessionId: "fresh-or-resumed-thread", prompt: [] },
{ approvalPolicy: "on-request", sandboxPolicy: policy },
{ model: "test-model", effort: "low" },
null, false, "/workspace", [], undefined, undefined,
);
}
test("ACP forwards workspace networking to the actual Codex turn without changing confinement", async () => {
const policy = { type: "workspaceWrite", writableRoots: ["/workspace"], networkAccess: false };
const result = await turnPolicy(policy, { PAPERCLIP_CODEX_ACP_NETWORK_ACCESS: "true" });
assert.equal(result.sandboxPolicy.networkAccess, true);
assert.equal(result.sandboxPolicy.type, "workspaceWrite");
assert.equal(result.sandboxPolicy.writableRoots, policy.writableRoots);
assert.equal(result.approvalPolicy, "on-request");
assert.equal(policy.networkAccess, false, "must not mutate the shared mode preset");
});
test("ACP preserves operator and execution-target network denials", async () => {
const policy = { type: "workspaceWrite", networkAccess: true };
for (const env of [
{ PAPERCLIP_CODEX_ACP_NETWORK_ACCESS: "false" },
{ PAPERCLIP_CODEX_ACP_NETWORK_ACCESS: "true", PAPERCLIP_RUNNER_NETWORK_ACCESS: "disabled" },
]) {
assert.equal((await turnPolicy(policy, env)).sandboxPolicy.networkAccess, false);
}
});
test("ACP leaves other sandbox modes and callers without a valid override unchanged", async () => {
for (const policy of [{ type: "readOnly", networkAccess: false }, { type: "dangerFullAccess" }]) {
assert.equal((await turnPolicy(policy, { PAPERCLIP_CODEX_ACP_NETWORK_ACCESS: "true" })).sandboxPolicy, policy);
}
const policy = { type: "workspaceWrite", networkAccess: false };
for (const value of [undefined, "", "1", "invalid"]) {
assert.equal((await turnPolicy(policy, { PAPERCLIP_CODEX_ACP_NETWORK_ACCESS: value })).sandboxPolicy, policy);
}
});