| 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 |