Skip to content

Commit 5150ea9

Browse files
qozleclaude
andauthored
fix: snapshot PR's .claude/ to .claude-pr/ before security restore (#1172)
When a PR modifies files under .claude/, the security restore in restoreConfigFromBase() overwrites them with the base branch version — correct for execution safety, but it means review agents never see what the PR actually changes. Before deleting the PR-controlled .claude/ tree, copy it to .claude-pr/. Review agents can read .claude-pr/ to inspect the PR's hooks, MCP configs, settings, and CLAUDE.md without those files ever being executed. The snapshot is taken before the security delete so it captures the full PR-authored version. Fixes #1134. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent eb8baa4 commit 5150ea9

File tree

1 file changed

+14
-1
lines changed

1 file changed

+14
-1
lines changed

src/github/operations/restore-config.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { execFileSync } from "child_process";
2-
import { rmSync } from "fs";
2+
import { cpSync, existsSync, rmSync } from "fs";
33

44
// Paths that are both PR-controllable and read from cwd at CLI startup.
55
//
@@ -44,6 +44,19 @@ export function restoreConfigFromBase(baseBranch: string): void {
4444
`Restoring ${SENSITIVE_PATHS.join(", ")} from origin/${baseBranch} (PR head is untrusted)`,
4545
);
4646

47+
// Snapshot the PR's .claude/ tree to .claude-pr/ before deleting it.
48+
// This lets review agents inspect what the PR actually changes (CLAUDE.md,
49+
// settings, hooks, MCP configs) without those files ever being executed.
50+
// The snapshot is taken before the security delete so it captures the
51+
// PR-authored version.
52+
rmSync(".claude-pr", { recursive: true, force: true });
53+
if (existsSync(".claude")) {
54+
cpSync(".claude", ".claude-pr", { recursive: true });
55+
console.log(
56+
"Preserved PR's .claude/ → .claude-pr/ for review agents (not executed)",
57+
);
58+
}
59+
4760
// Delete PR-controlled versions BEFORE fetching so the attacker-controlled
4861
// .gitmodules is absent during the network operation. If git reads .gitmodules
4962
// during fetch (fetch.recurseSubmodules=on-demand, the git default), it will

0 commit comments

Comments
 (0)