返回 CodeWhale
helper-policy.test.mjs
根目录 / crates / tui / plugins / computer-use / tests / helper-policy.test.mjs
1 // The desktop helper applies its own policy to every request that reaches its
2 // socket, and never runs a launch command read from its state files.
3 import { test } from "node:test";
4 import assert from "node:assert/strict";
5 import fs from "node:fs";
6 import os from "node:os";
7 import path from "node:path";
8 import url from "node:url";
9
10 const ROOT = path.resolve(path.dirname(url.fileURLToPath(import.meta.url)), "..");
11 const stateDir = fs.mkdtempSync(path.join(os.tmpdir(), "cu-helper-policy-"));
12 process.env.CODEWHALE_CU_STATE_DIR = stateDir;
13 process.env.CODEWHALE_CU_TEST_BACKEND = path.join(ROOT, "tests", "fixtures", "fake-backend.mjs");
14
15 const { handle } = await import("../src/app-handler.mjs");
16 const { launchArgv, readRegistration, writeRegistration } = await import("../src/app-socket.mjs");
17
18 test("app.json launch without MAC is ignored", () => {
19 const planted = { id: "net.codewhale.computer-use", path: "/Applications/Codewhale Computer Use.app", launch: ["/bin/sh", "-c", "touch /tmp/planted"] };
20 assert.deepEqual(launchArgv(planted, "darwin"), ["open", "-g", "-a", "/Applications/Codewhale Computer Use.app"]);
21 for (const bad of [{ launch: ["/bin/sh"] }, { path: "relative.app" }, { path: "/tmp/not-a-bundle" }]) {
22 assert.equal(launchArgv(bad, "darwin"), null, JSON.stringify(bad));
23 }
24 writeRegistration({ id: "x", launch: ["/bin/sh", "-c", "true"] });
25 assert.equal(readRegistration(), null, "a registration without a bundle path is not launchable");
26 });
27
28 test("the helper refuses scripts its policy refuses, whoever connects", async () => {
29 const r = await handle({ tool: "app_script", args: { script: 'do shell script "id"' } }, { computerId: "local", sessionId: "raw" });
30 assert.equal(r.ok, false);
31 assert.equal(r.error.code, "script_refused");
32 });
33
34 test("the helper honors a recorded deny, whoever connects", async () => {
35 fs.writeFileSync(path.join(stateDir, "consent.json"), JSON.stringify({ version: 1, computers: { local: { apps: {
36 "name:terminal": { decision: "deny", at: new Date().toISOString() },
37 } } } }));
38 for (const req of [
39 { tool: "open_application", args: { name: "Terminal" } },
40 { tool: "kill_app", args: { name: "Terminal" } },
41 { tool: "app_script", args: { script: 'tell application "Terminal" to activate' } },
42 ]) {
43 const r = await handle(req, { computerId: "local", sessionId: "raw" });
44 assert.equal(r.error?.code, req.tool === "app_script" ? "script_refused" : "app_denied", JSON.stringify(req));
45 }
46 const other = await handle({ tool: "app_script", args: { script: 'tell application "Notes" to activate' } }, { computerId: "local", sessionId: "raw" });
47 assert.notEqual(other.error?.code, "app_denied");
48 });
49
49 lines Plain Text