# git-safe Hardened git clone tool for Pi. ## Install / load Loaded through the root `pi-ext` package. See [root README](../../README.md). ## Commands / tools / settings - Commands: none - Tools: - `git_clone_safe(url, path, branch?, sparse?)` - `url`: Git repository URL - `path`: local target directory, absolute or relative to cwd - `branch`: optional branch name - `sparse`: optional repository-relative sparse checkout paths - Hooks: - `session_start` - `session_shutdown` - `tool_call` for agent-issued `bash` - `tool_result` for `read` - Settings: - `git-safe.sizeLimitBytes`: maximum repository content in bytes, checked before and after checkout. Env `PI_GIT_SAFE_SIZE_LIMIT_BYTES`. Default `524288000` (500 MiB). - `git-safe.fileCountLimit`: maximum file count, checked before and after checkout. Env `PI_GIT_SAFE_FILE_COUNT_LIMIT`. Default `10000`. - `git-safe.cloneTimeoutMs`: milliseconds before `git clone` is killed. Env `PI_GIT_SAFE_CLONE_TIMEOUT_MS`. Default `60000`. Each value must be a positive integer; `cloneTimeoutMs` is at most `2147483647`. Settings files need JSON numbers; env vars accept numeric strings. Precedence: env, trusted project `.pi/settings.json`, user `~/.pi/agent/settings.json`, default. Project settings apply only when the project is trusted. Values resolve on each `git_clone_safe` call. An invalid value fails the clone with an error naming the key and source; it never falls back to a lower source or the default. The 30s checkout timeout is fixed. ## Behavior Agent-issued Bash commands receive noninteractive Git editor settings: - `GIT_EDITOR=true` - `GIT_SEQUENCE_EDITOR=true` - `GIT_MERGE_AUTOEDIT=no` A generated Git executable wrapper receives parsed arguments from normal Bash calls, `command git`, `env git`, `xargs git`, nested Bash, and unqualified child-process PATH lookup. The first Bash call creates it with fixed Node and Git paths; later calls and reloads reuse it. It resets the editor environment immediately before invoking real Git and delegates ordinary commands unchanged. Bash `git()` and `git.exe()` functions route direct calls to that wrapper. Windows paths are converted for Git Bash, and common `git.exe` shell forms route through the wrapper. The layers are defense in depth, not a security boundary; unrestricted Bash can still address or construct alternate executable paths. On Windows, a non-Bash child process that explicitly launches `git.exe` can bypass the script wrapper because the extension does not ship a native PE launcher. `git_clone_safe` clones a repository with checks around hook execution, symlinks, LFS filters, disk use, and credential prompts. Operational behavior: - rejects root, home, paths with depth less than 2, exact `/tmp`, `/var`, `/etc`, `/usr`, and paths starting with `/sys` or `/proc` - refuses an existing target dir unless it contains `.pi-git-safe` - removes and replaces prior `.pi-git-safe` clone dirs - runs `git clone --no-checkout --depth 1 --single-branch -c core.symlinks=false` - passes `--branch` when `branch` is provided - when `sparse` paths are provided, adds `--filter=blob:none` and `--sparse` to clone args - rejects unsafe sparse paths: empty paths, absolute paths, `.`, `..`, traversal, and null bytes - disables credential prompts with `GIT_TERMINAL_PROMPT=0` and `GIT_TERMINAL_PROMPT_AUTOCHECK=0` - sets repo-local `core.hooksPath` to an empty hooks dir - sets repo-local `lfs.skipSmudge=true` - writes `.git/info/attributes` with `* -filter` - removes `.git/hooks` - estimates checkout size with `git ls-tree -r -l HEAD` - rejects content over `git-safe.sizeLimitBytes`, default 500 MiB - rejects trees over `git-safe.fileCountLimit`, default 10,000 files - scans `git ls-files -s` for symlinks before checkout - checks out with the empty hooks dir enforced - kills clone after `git-safe.cloneTimeoutMs`, default 60s, and checkout after 30s - writes `.pi-git-safe` marker after successful checkout - returns canonical clone path, URL, optional branch, file count, total byte count, symlink count, symlink paths, warnings, and optional sparse paths - recovers marked clone dirs on demand by checking ancestors of each `read` path - wraps text `read` results from tracked clone dirs in `` markers with a git-specific warning preamble - warns when `.git/config` contains `include` or `includeIf` - warns when symlinks are present; `core.symlinks=false` stores them as text files ## Debug Opt in through [debug contract](../DEBUG.md). Safe events: `session.start`, `session.shutdown`, `git.policy.initialize.start`, `git.policy.initialize.finish`, `git.policy.initialize.error`, `clone.start`, `clone.finish`, `clone.error`.