ACP / MCP 完善

This commit is contained in:
2026-06-11 12:43:18 +08:00
parent 1415d3b43b
commit 0561e126cd
22 changed files with 786 additions and 219 deletions
+5 -44
View File
@@ -1,55 +1,16 @@
// permission.go 实现 ACP fs/* method 的路径安全校验。
// permission.go 暴露 ACP fs/* method 的路径安全校验入口
//
// 规则(spec §5.6):
// 1. 路径必须以 cwd_path 为前缀(防 ../../etc/passwd)
// 2. 解析 symlink 后的真实路径必须仍在 cwd_path 下(防 symlink 逃逸)
//
// 三重校验:filepath.Abs + strings.HasPrefix + filepath.EvalSymlinks。
// 具体校验逻辑在 internal/acp/pathsafe 子包(与 handlers 子包共用同一实现,
// 详见 pathsafe 包注释)。
package acp
import (
"path/filepath"
"strings"
"github.com/yan1h/agent-coding-workflow/internal/infra/errs"
"github.com/yan1h/agent-coding-workflow/internal/acp/pathsafe"
)
// ResolveSafePath 校验 requested 是否安全(在 cwd 内)。
// cwd 必须是绝对路径;requested 可以是绝对或相对(相对时基于 cwd 解析)。
// 返回校验通过后的绝对路径(含 symlink 解析);失败返回 CodeAcpFsPathOutsideWorktree。
func ResolveSafePath(cwd, requested string) (string, error) {
if !filepath.IsAbs(cwd) {
return "", errs.New(errs.CodeAcpFsPathOutsideWorktree, "cwd must be absolute")
}
cleanCwd := filepath.Clean(cwd)
var abs string
if filepath.IsAbs(requested) {
abs = filepath.Clean(requested)
} else {
abs = filepath.Clean(filepath.Join(cleanCwd, requested))
}
if !hasPathPrefix(abs, cleanCwd) {
return "", errs.New(errs.CodeAcpFsPathOutsideWorktree, "path outside worktree")
}
real, err := filepath.EvalSymlinks(abs)
if err == nil {
if !hasPathPrefix(real, cleanCwd) {
return "", errs.New(errs.CodeAcpFsPathOutsideWorktree, "path resolves outside worktree via symlink")
}
}
return abs, nil
}
// hasPathPrefix reports whether abs is equal to base or under base. Trailing
// separator handling is critical: we want "/foo/bar" to be inside "/foo" but
// NOT inside "/fo".
func hasPathPrefix(abs, base string) bool {
if abs == base {
return true
}
sep := string(filepath.Separator)
return strings.HasPrefix(abs, base+sep)
return pathsafe.ResolveSafePath(cwd, requested)
}