Files
paseo/REPORT-claude-permission-tests.md
T
Mohamed BoudraandClaude Opus 4.5 78dec91bdf Investigate Claude permission test failures in daemon E2E tests
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>
2025-12-25 17:57:40 +07:00

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:

  1. Creates a temporary config directory with custom settings.json
  2. Sets CLAUDE_CONFIG_DIR environment variable to point to it
  3. Includes ask: ["Bash(rm:*)"] so rm commands request permission
  4. 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:

  1. Start a daemon server via createTestPaseoDaemon()
  2. The daemon runs in-process (not a separate process)
  3. No temp config directory is set up
  4. The Claude SDK uses settingSources: ["user", "project"] (line 665 of claude-agent.ts)
  5. SDK reads ~/.claude/settings.json which has allow: ["Bash(rm:*)"]
  6. rm commands auto-execute without permission prompt

Evidence

User's ~/.claude/settings.json:

{
  "permissions": {
    "allow": [
      "Bash(rm:*)",   // <-- This allows rm without permission
      ...
    ],
    ...
  }
}

Solution Options

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:

  1. Set settingSources: [] to ignore user settings
  2. Provide explicit permission rules via SDK options

This would require changes to how claude-agent.ts configures the SDK.

Could detect if user has Bash(rm:*) in allow list and skip tests. This is fragile and doesn't actually test the permission flow.

Option 1 is simplest and most consistent with how claude-agent.test.ts already works:

  1. Create a shared useTempClaudeConfigDir() utility in test-utils
  2. Use it in the Claude permission tests in daemon.e2e.test.ts
  3. 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 setup
  • packages/server/src/server/agent/providers/claude-agent.ts:651-670 - SDK options including settingSources
  • packages/server/src/server/daemon.e2e.test.ts:903-1094 - skipped permission tests
  • packages/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 - settingSources docs
  • ~/.claude/settings.json - user's actual settings with allow: ["Bash(rm:*)"]