From 4a458feae125d88e782cc49810cb6b495673c566 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 28 Mar 2026 13:18:44 +0000 Subject: [PATCH] =?UTF-8?q?test:=20agent=20=E2=80=94=20.env=20read=20prote?= =?UTF-8?q?ction=20and=20analyst=20write=20denial?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Verify security-relevant agent permission defaults: builder agent asks before reading .env files (preventing accidental secret exposure), and analyst agent denies file modification tools (edit/write/todowrite/todoread). Co-Authored-By: Claude Opus 4.6 (1M context) https://claude.ai/code/session_01Wp9YaEvw6jAAL73VVdXFxA --- packages/opencode/test/agent/agent.test.ts | 49 ++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/packages/opencode/test/agent/agent.test.ts b/packages/opencode/test/agent/agent.test.ts index 373a1fd9db..efe4fbea4f 100644 --- a/packages/opencode/test/agent/agent.test.ts +++ b/packages/opencode/test/agent/agent.test.ts @@ -810,3 +810,52 @@ test("analyst prompt contains /data-viz skill", async () => { }, }) }) + +// --- .env read protection tests --- + +test("builder agent asks for .env file reads but allows regular files", async () => { + await using tmp = await tmpdir() + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const builder = await Agent.get("builder") + expect(builder).toBeDefined() + // .env files require user approval (security: prevents accidental secret exposure) + expect(PermissionNext.evaluate("read", ".env", builder!.permission).action).toBe("ask") + expect(PermissionNext.evaluate("read", ".env.local", builder!.permission).action).toBe("ask") + expect(PermissionNext.evaluate("read", ".env.production", builder!.permission).action).toBe("ask") + expect(PermissionNext.evaluate("read", "config/.env.staging", builder!.permission).action).toBe("ask") + // Regular files are allowed without prompting + expect(PermissionNext.evaluate("read", "src/index.ts", builder!.permission).action).toBe("allow") + expect(PermissionNext.evaluate("read", "package.json", builder!.permission).action).toBe("allow") + // .env.example is explicitly allowed (safe to share) + expect(PermissionNext.evaluate("read", ".env.example", builder!.permission).action).toBe("allow") + }, + }) +}) + +// --- analyst agent write denial tests --- + +test("analyst agent denies file modification and todo tools", async () => { + await using tmp = await tmpdir() + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const analyst = await Agent.get("analyst") + expect(analyst).toBeDefined() + // Analyst is read-only — file modification tools should be denied. + // The analyst config starts with "*": "deny" then selectively allows + // read-only tools. edit/write/todowrite are not in the allow list, + // so they fall through to the catch-all deny. + expect(evalPerm(analyst, "edit")).toBe("deny") + expect(evalPerm(analyst, "write")).toBe("deny") + expect(evalPerm(analyst, "todowrite")).toBe("deny") + expect(evalPerm(analyst, "todoread")).toBe("deny") + // Read operations are explicitly allowed after the "*": "deny" base, + // so last-match-wins produces "allow" + expect(evalPerm(analyst, "read")).toBe("allow") + expect(evalPerm(analyst, "grep")).toBe("allow") + expect(evalPerm(analyst, "glob")).toBe("allow") + }, + }) +})