Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions internal/fs/git_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,6 @@ func UntrustedRepositoryGitCommand(root string, args ...string) *exec.Cmd {
}
command := exec.Command("git", append(hardened, args...)...)
command.Env = append(os.Environ(), "GIT_OPTIONAL_LOCKS=0")
hideChildConsole(command)
return command
}
8 changes: 8 additions & 0 deletions internal/fs/git_command_other.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//go:build !windows

package fs

import "os/exec"

// hideChildConsole 在非 Windows 平台为空操作;可见控制台窗口问题仅 Windows 存在。
func hideChildConsole(*exec.Cmd) {}
28 changes: 28 additions & 0 deletions internal/fs/git_command_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//go:build windows

// Windows 控制台抑制 —— 与 git_command.go 同包的平台侧实现
//
// 威胁: aoci 常以无控制台形态运行(如 MCP stdio 由宿主拉起)。此时 exec 拉起
// 控制台子进程(git.exe)时,Windows 会为子进程新分配一个可见控制台窗口——
// 刷新期间反复弹窗并抢占用户焦点。
// 边界: CREATE_NO_WINDOW 只抑制控制台分配,不改变 stdio 句柄继承;宿主提供的
// 管道与真实终端下的交互输出不受影响。HideWindow(STARTF_USESHOWWINDOW+SW_HIDE)
// 作为兜底。调用方环境变量属操作者自身信任域,不在此处覆盖。
package fs

import (
"os/exec"
"syscall"
)

// createNoWindow 即 Win32 CREATE_NO_WINDOW(0x08000000)。
const createNoWindow = 0x08000000

// hideChildConsole 阻止 Windows 为 git.exe 子进程新建可见控制台窗口。
func hideChildConsole(cmd *exec.Cmd) {
if cmd.SysProcAttr == nil {
cmd.SysProcAttr = &syscall.SysProcAttr{}
}
cmd.SysProcAttr.HideWindow = true
cmd.SysProcAttr.CreationFlags |= createNoWindow
}
25 changes: 25 additions & 0 deletions internal/fs/git_command_windows_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//go:build windows

package fs

import (
"testing"
)

// 无控制台父进程(如 MCP stdio 宿主)拉起 git.exe 时,Windows 会为子进程新分配
// 可见控制台窗口。构造出的命令必须携带 CREATE_NO_WINDOW+HideWindow;本断言把
// 窗口抑制固定为构造器不变量,防止后续重构静默丢失。
func TestUntrustedRepositoryGitCommandSuppressesChildConsoleWindow(t *testing.T) {
command := UntrustedRepositoryGitCommand(t.TempDir(), "rev-parse", "--show-toplevel")
attr := command.SysProcAttr
if attr == nil {
t.Fatalf("git 调用必须设置 SysProcAttr 以抑制子进程控制台窗口")
}
if !attr.HideWindow {
t.Fatalf("git 调用必须设置 HideWindow")
}
if attr.CreationFlags&createNoWindow == 0 {
t.Fatalf("git 调用必须携带 CREATE_NO_WINDOW(0x%x),实际 CreationFlags=0x%x",
createNoWindow, attr.CreationFlags)
}
}