fix(agent-sdk): pass settingSources [] so SDK ignores cloned PR .claude/settings.json#195
Conversation
…de/settings.json The Agent SDK query() runs with cwd set to the cloned PR working tree but never passed settingSources, so the SDK loaded user/project/local filesystem settings (.claude/settings.json) by default. A hostile fork PR could ship a .claude/settings.json with a SessionStart shell hook that fires in the CLI subprocess (which carries GH_TOKEN/GITHUB_TOKEN and ANTHROPIC creds in env) before any tool gate, enabling credential exfiltration / RCE. Pin settingSources to [] to disable all three filesystem tiers. Adds a regression test asserting options.settingSources deep-equals [] (catches both removal and a flip to a permissive value). Defense-in-depth follow-ups (rm .claude/ from workDir, CLAUDE.md invariant bullet, call-site audit) are deferred. Closes #191 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThis PR hardens the Claude Agent SDK invocation in ChangesFilesystem Settings Isolation
Estimated code review effort🎯 2 (Simple) | ⏱️ ~8 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
Hardens the Claude Agent SDK call site by explicitly setting settingSources: [] so the SDK does not load .claude/settings.json (or user/local equivalents) from the cloned PR working tree, which could otherwise execute attacker-controlled SessionStart shell hooks with the GitHub App installation token in env.
Changes:
- Add
settingSources: []toqueryOptionsinsrc/core/executor.tswith an inline comment documenting the attack path and regression warning. - Add a regression test in
test/core/executor.test.tsasserting deep equality ofoptions.settingSourcesto[].
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| src/core/executor.ts | Pins settingSources: [] next to existing disallowedTools hardening to block filesystem-tier settings loading from cwd. |
| test/core/executor.test.ts | New test verifying the queryOptions deep-equals [] for settingSources, catching both removal and permissive flips. |
Summary
Closes #191.
The Claude Agent SDK
query()runs withcwdset to the cloned PR working tree (src/core/checkout.tsclones the PR head intoworkDir, andsrc/core/executor.tspassescwd: workDir) but never passedsettingSources. Per the SDK contract (SettingSource = 'user' | 'project' | 'local',node_modules/@anthropic-ai/claude-agent-sdk/sdk.d.ts), omitting it loads all three filesystem tiers by default.Attack path closed
A fork PR from any GitHub user can ship a hostile
.claude/settings.jsonwith aSessionStartshell hook. The webhook router (src/webhook/router.ts) gates onALLOWED_OWNERS, not PR author, so any maintainer comment triggers the bot on that PR. The hook fires in the CLI subprocess — which carriesGH_TOKEN/GITHUB_TOKEN(App installation token) andANTHROPIC_*creds in env — before any tool gate, bypassing every existing prompt-injection defense. This is a credential-exfil / RCE path against the daemon.Fix
Pin
settingSources: []in thequeryOptionsobject (src/core/executor.ts, beside the existingdisallowedTools: ["ToolSearch"]idiom). This disables the user/project/local filesystem tiers, so a hostile.claude/settings.jsonis never loaded. The managed-settings policy tier (operator-controlled, outside the cloned tree) is unaffected.Changes
src/core/executor.ts— addsettingSources: []+ comment documenting the attack path and a regression warning.test/core/executor.test.ts— newexecuteAgent: filesystem settings isolationtest assertingoptions.settingSourcesdeep-equals[](catches both removal and a flip to a permissive value like["user"]).Verification
Expected: [] / Received: undefined), green with it.scripts/test-isolated.shharness). Typecheck clean; prettier clean; no em-dash.Deferred (defense-in-depth follow-ups, out of scope here)
Per the issue's suggested next steps 2/4/5: a CLAUDE.md security-invariant bullet, removing
.claude/fromworkDirafter checkout, and a periodic SDK call-site audit. These reduce the broader "untrusted tree at cwd" surface but are not required to close #191.🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes
Tests