Root cause: daemon E2E tests read user's real ~/.claude/settings.json which has Bash(rm:*) in allow list, causing rm commands to auto-execute without permission prompts. Direct claude-agent.test.ts works because it uses useTempClaudeConfigDir() to create isolated settings with ask: ["Bash(rm:*)"] and sets CLAUDE_CONFIG_DIR env var. Fix: Add temp config setup to daemon tests (same pattern as direct tests) or use settingSources: [] for SDK isolation mode. See REPORT-claude-permission-tests.md for full analysis. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
4.5 KiB
Investigation: Claude Provider Permissions in Daemon E2E Tests
Root Cause
The Claude permission tests in daemon.e2e.test.ts don't work because they read the user's real ~/.claude/settings.json which has Bash(rm:*) in the allow list. This causes rm commands to execute without requesting permission.
Why Direct Tests Work (claude-agent.test.ts)
The direct tests use useTempClaudeConfigDir() at packages/server/src/server/agent/providers/claude-agent.test.ts:57-87:
function useTempClaudeConfigDir(): () => void {
const previousConfigDir = process.env.CLAUDE_CONFIG_DIR;
const sourceConfigDir = previousConfigDir ?? path.join(os.homedir(), ".claude");
const configDir = mkdtempSync(path.join(os.tmpdir(), "claude-config-"));
const settings = {
permissions: {
allow: [],
deny: [],
ask: ["Bash(rm:*)"], // <-- Forces rm to request permission
additionalDirectories: [],
},
sandbox: { enabled: true, autoAllowBashIfSandboxed: false },
};
writeFileSync(path.join(configDir, "settings.json"), settingsText, "utf8");
writeFileSync(path.join(configDir, "settings.local.json"), settingsText, "utf8");
copyClaudeCredentials(sourceConfigDir, configDir);
process.env.CLAUDE_CONFIG_DIR = configDir; // <-- SDK reads from this
return () => { /* cleanup */ };
}
This:
- Creates a temporary config directory with custom
settings.json - Sets
CLAUDE_CONFIG_DIRenvironment variable to point to it - Includes
ask: ["Bash(rm:*)"]sormcommands request permission - The test's
beforeAll()calls this before any tests run
Why Daemon E2E Tests Fail
The daemon tests at packages/server/src/server/daemon.e2e.test.ts:910-1094:
- Start a daemon server via
createTestPaseoDaemon() - The daemon runs in-process (not a separate process)
- No temp config directory is set up
- The Claude SDK uses
settingSources: ["user", "project"](line 665 ofclaude-agent.ts) - SDK reads
~/.claude/settings.jsonwhich hasallow: ["Bash(rm:*)"] rmcommands auto-execute without permission prompt
Evidence
User's ~/.claude/settings.json:
{
"permissions": {
"allow": [
"Bash(rm:*)", // <-- This allows rm without permission
...
],
...
}
}
Solution Options
Option 1: Use CLAUDE_CONFIG_DIR in Daemon Tests (Recommended)
Modify createDaemonTestContext() or add a new function for permission tests:
function useTempClaudeConfigDir(): () => void {
// Same implementation as claude-agent.test.ts
// Set CLAUDE_CONFIG_DIR before test starts
}
// In permission test beforeAll:
beforeAll(() => {
restoreConfigDir = useTempClaudeConfigDir();
});
afterAll(() => {
restoreConfigDir?.();
});
Since the daemon runs in-process, process.env.CLAUDE_CONFIG_DIR should work.
Option 2: Use SDK's allowedTools Override
The Claude SDK has an allowedTools option that auto-allows specific tools. We could potentially use a combination of SDK options to override filesystem settings.
However, the SDK docs say settingSources: [] (empty) creates "SDK isolation mode" where no filesystem settings are loaded. We could:
- Set
settingSources: []to ignore user settings - Provide explicit permission rules via SDK options
This would require changes to how claude-agent.ts configures the SDK.
Option 3: Skip Tests When User Settings Conflict (Not Recommended)
Could detect if user has Bash(rm:*) in allow list and skip tests. This is fragile and doesn't actually test the permission flow.
Recommended Fix
Option 1 is simplest and most consistent with how claude-agent.test.ts already works:
- Create a shared
useTempClaudeConfigDir()utility in test-utils - Use it in the Claude permission tests in daemon.e2e.test.ts
- Ensure the temp config has
ask: ["Bash(rm:*)"]to force permission requests
The daemon runs in the same process, so setting CLAUDE_CONFIG_DIR before creating agents should work.
Files Analyzed
packages/server/src/server/agent/providers/claude-agent.test.ts:57-87- temp config setuppackages/server/src/server/agent/providers/claude-agent.ts:651-670- SDK options includingsettingSourcespackages/server/src/server/daemon.e2e.test.ts:903-1094- skipped permission testspackages/server/src/server/test-utils/paseo-daemon.ts- daemon creation (no config override)node_modules/@anthropic-ai/claude-agent-sdk/entrypoints/agentSdkTypes.d.ts:983-991-settingSourcesdocs~/.claude/settings.json- user's actual settings withallow: ["Bash(rm:*)"]