Repository navigation
fix(sandbox): give sandboxed prek its own home - #1792
Merged
Merged
Conversation
potiuk
enabled auto-merge (squash)
October 24, 2026 22:11
potiuk
force-pushed
the
fix/sandbox-prek-home
branch
from
October 24, 2026 22:26
3259ae4 to
490034f
Compare
potiuk
disabled auto-merge
October 24, 2026 22:29
potiuk
force-pushed
the
fix/sandbox-prek-home
branch
from
October 24, 2026 22:55
9e1d715 to
17b554e
Compare
potiuk
enabled auto-merge (squash)
October 24, 2026 22:56
A commit made in a terminal runs prek's git hooks outside the sandbox from their environments under ~/.cache/prek, which the ~/.cache grant made sandbox-writable: a sandboxed command could plant code there that ran unsandboxed on the next terminal commit. Nothing excluded from the sandbox runs prek, so the settings env (seen only by Claude Code's own commands, never a terminal) does the split: PREK_HOME=~/.cache/prek-sandbox for sandboxed prek, and Edit(~/.cache/prek/**) keeps sandboxed commands out of the terminal's home. sandbox-lint requires both; verify check 19d, install Step U, the update drift item and the docs cover the switch. Generated-by: Claude (Opus 5.5)
…andbox Review follow-up. Claude Code's env also reaches a command it runs outside the sandbox (an approved bypass, often a signed commit), whose hooks would then run the environments sandboxed commands wrote under ~/.cache/prek-sandbox. The whole-user git-hook dispatcher now drops a PREK_HOME pointing there when SANDBOX_RUNTIME is absent, before chaining to the repository's own hook, so prek falls back to the host-only ~/.cache/prek. Sandboxed code that unsets SANDBOX_RUNTIME only makes its own prek fail to write there. Per-project scope has no dispatcher, so verify check 19d warns about it and the docs say not to bypass the sandbox for a commit there. Adds the first dispatcher tests. Generated-by: Claude (Opus 5.5)
…s env Replaces the settings-env approach. env.PREK_HOME also reached commands Claude Code runs outside the sandbox, which then needed stripping again. Instead the whole-user git-hook dispatcher gives a hook it runs outside the sandbox (no SANDBOX_RUNTIME: a terminal commit or a bypassed one) PREK_HOME=~/.local/share/prek-host, which no sandbox grant covers, while sandboxed hooks keep the default ~/.cache/prek. An operator's own PREK_HOME outside ~/.cache is kept. The settings env, the Edit(~/.cache/prek/**) deny and the sandbox-lint rule are gone; verify check 19d now checks the dispatcher copy and warns for per-project scope. Generated-by: Claude (Opus 5.5)
Review follow-up. The tools a hook calls keep caches of their own (pip, go, npm, and uv without a configured cache-dir follow XDG_CACHE_HOME, ~/.cache by default on Linux), and installing from a cache the sandbox wrote is the same escape. Outside the sandbox the dispatcher now also points XDG_CACHE_HOME at ~/.local/share/prek-host/cache, with the same rule as PREK_HOME: an operator's own value outside ~/.cache is kept. Generated-by: Claude (Opus 5.5)
…passed hooks Reverts the dispatcher-only switch. Many repositories set their own core.hooksPath (often left by `prek install`), which skips the whole-user dispatcher, so a terminal commit there ran prek from the sandbox-writable ~/.cache/prek. The settings env protects terminal commits in every repository: sandboxed prek uses PREK_HOME=~/.cache/prek-sandbox, and Edit(~/.cache/prek/**) keeps sandboxed commands out of the terminal's home (sandbox-lint requires both again). The dispatcher keeps covering hooks Claude Code runs outside the sandbox (host-only PREK_HOME and XDG_CACHE_HOME), and verify now flags a repository-level core.hooksPath that skips it. Generated-by: Claude (Opus 5.5)
Some prek install runs set a repository-level core.hooksPath to the repository's own .git/hooks, which overrides the global one and skips the whole-user dispatcher, and with it the host-only prek home for hooks run outside the sandbox. The session-start hook now removes that value when the dispatcher is installed (it chains to .git/hooks anyway) and tells the user; any other value is reported. It reads and writes only the global config and the repository's config file through `git config --global` / `--file`, so no repository hooks or fsmonitor run, refuses a symlinked .git or config, and leaves linked worktrees to their main checkout. Generated-by: Claude (Opus 5.5)
Review follow-up. .git sits in the sandbox-writable project, so handing the repository config to `git config --file` left a window in which .git could be swapped for a symlink and git would write a file named config wherever it pointed. The hooks-path cleanup now opens .git and config with O_NOFOLLOW through one descriptor, parses the [core] hooksPath line itself, and renames a fresh temporary file inside that directory; a config with includes or anything unusual is only reported. Values that reach the message (and the model's context) are quoted, stripped of control characters and truncated. Generated-by: Claude (Opus 5.5)
Generated-by: Claude (Opus 5.5)
potiuk
force-pushed
the
fix/sandbox-prek-home
branch
from
October 24, 2026 23:01
df9a59a to
9f478f1
Compare
…anup Review follow-up. A key after a section header on the same line, or a header the parser does not understand, now means "only report"; a [core "x"] subsection is no longer taken for [core]; a config that does not decode is reported instead of skipped; and the rewritten text must read back plain with no hooksPath left, or nothing is written. Generated-by: Claude (Opus 5.5)
CodeQL: overly permissive file permissions. The temporary file was created 0644; it is now created 0600 and given the original config's mode before the rename, whatever the umask. Generated-by: Claude (Opus 5.5)
potiuk
added a commit
that referenced
this pull request
Oct 25, 2026
Ships the fixes merged since 0.9.0.dev202610241347 to marketplace installs: the container-gateway hook hardening (#1784, #1789, #1790, #1793, #1804), host-only uv state (#1785), session-start cleanup of the stale uv grant (#1788), the trusted session-start interpreter (#1791), the sandboxed prek home (#1792) and per-machine gateway hook registration (#1797). Generated-by: Claude (Opus 5.5)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A commit made in a terminal runs prek's git hooks outside the sandbox, from environments under
~/.cache/prek, which the~/.cache/grant made sandbox-writable. A sandboxed command could plant code there that ran unsandboxed on the next terminal commit."env": {"PREK_HOME": "~/.cache/prek-sandbox"}gives sandboxed prek its own home (only Claude Code's commands see it, never a terminal), andEdit(~/.cache/prek/**)keeps sandboxed commands out of the terminal's home. sandbox-lint requires both. This protects terminal commits in every repository, including ones with their owncore.hooksPath.envalso reaches commands Claude Code runs outside the sandbox. The whole-user git-hook dispatcher gives a hook it runs with noSANDBOX_RUNTIMEa host-onlyPREK_HOME(~/.local/share/prek-host) andXDG_CACHE_HOME(for the pip, go, npm and uv caches hooks use). Verify now flags a repository-levelcore.hooksPaththat skips the dispatcher, and warns for per-project scope.core.hooksPathpointing at the repository's own.git/hooks(someprek installruns set one) skips the whole-user dispatcher; magpie-setup's session-start hook now removes it, since the dispatcher chains there anyway, and reports any other value.🤖 Generated with Claude Code