Files
zero/internal/worktrees/run_git_unix.go
KRATOSandClaude Opus 4.8 060d38ce71 feat(security): path-jail primitive, credential-store locking, worktree git hardening (#891)
Split 1/3 of #829, carved out as the independently-reversible hardening layer
with no dependency on the orchestration work that stacks on top of it.

- internal/pathjail: os.Root handle-relative path confinement — ancestor
  traversal is confined, the final component is checked for reparse points
  (including a name written with a trailing separator), unprivileged Windows
  junctions are covered, temp-name fragments are rejected before they can
  escape the directory, whitespace-bearing paths are confined as spelled, and
  temp files stay unpredictable and exclusive.
- internal/credstore: file locking around the credential read-modify-write —
  exclusive for writers, shared for readers, so a reader cannot race a publish.
- internal/worktrees: git invocation hardening.

pathjail has no importers in this PR by design; internal/memory and
internal/specialist adopt it in the stacked PRs (2/3 and 3/3).


Origin-Session: local-de382f | Claude Code | 4 prompts
Origin-Snapshot: 52db9b74dad5

Co-authored-by: Claude Opus 4.8 <[email protected]>
2026-08-15 20:24:08 +08:00

40 lines
1.3 KiB
Go

//go:build !windows
package worktrees
import (
"errors"
"os/exec"
"syscall"
"time"
)
// worktreeWaitDelay bounds how long Wait blocks for the child's stdout/stderr
// pipes to drain after the process exits or its context is cancelled, so a
// leaked grandchild cannot hang the parent past cancel/timeout. Var (not const)
// so tests can shorten it.
var worktreeWaitDelay = 2 * time.Second
// hardenWorktreeGit makes a git subprocess killable as a single unit. Setpgid
// puts the child into its own process group, so on cancel/timeout we signal the
// whole group (negative pid) — any long-running git subprocess dies with it
// instead of being orphaned. WaitDelay is the backstop if a grandchild still
// holds a pipe after the group is killed. Must be called before command.Run.
func hardenWorktreeGit(command *exec.Cmd) {
if command.SysProcAttr == nil {
command.SysProcAttr = &syscall.SysProcAttr{}
}
command.SysProcAttr.Setpgid = true
command.WaitDelay = worktreeWaitDelay
command.Cancel = func() error {
if command.Process == nil {
return nil
}
// Negative pid targets the whole process group led by the child.
if err := syscall.Kill(-command.Process.Pid, syscall.SIGKILL); err != nil && !errors.Is(err, syscall.ESRCH) {
return err
}
return nil
}
}