diff --git a/browse/src/server.ts b/browse/src/server.ts index 110b9d3eab..3b1bcdef95 100644 --- a/browse/src/server.ts +++ b/browse/src/server.ts @@ -483,7 +483,7 @@ function spawnClaude(userMessage: string, extensionUrl?: string | null, forTabId // Never resume — each message is a fresh context. Resuming carries stale // page URLs and old navigation state that makes the agent fight the user. const args = ['-p', prompt, '--model', 'opus', '--output-format', 'stream-json', '--verbose', - '--allowedTools', 'Bash,Read,Glob,Grep']; + '--allowedTools', 'Bash,Read,Glob,Grep,Write']; addChatEntry({ ts: new Date().toISOString(), role: 'agent', type: 'agent_start' }); diff --git a/browse/test/sidebar-security.test.ts b/browse/test/sidebar-security.test.ts index 71f2190a0a..9ca52dc3d8 100644 --- a/browse/test/sidebar-security.test.ts +++ b/browse/test/sidebar-security.test.ts @@ -103,6 +103,16 @@ describe('Sidebar prompt injection defense', () => { expect(SERVER_SRC).toContain('refuse'); }); + // --- Allowed Tools (server + agent must stay in sync) --- + + test('server allowedTools includes Write', () => { + // Write does not expand attack surface beyond Bash (which can already + // write files). The server args are the source of truth — if Write is + // missing here, the sidebar agent cannot use it even if the agent + // fallback includes it, because the server-provided args take precedence. + expect(SERVER_SRC).toContain("'--allowedTools', 'Bash,Read,Glob,Grep,Write'"); + }); + // --- Sidebar Agent Arg Plumbing --- test('sidebar-agent uses queued args from server, not hardcoded', () => {