mirror of
https://github.com/Twigpine/zero.git
synced 2026-11-10 05:01:28 -10:00
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]>
40 lines
1.3 KiB
Go
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
|
|
}
|
|
}
|