Files
weichenandCursor fedf671a48
ci / vet-and-test (push) Successful in 5s
ci / docker-checkout-hostgateway (push) Successful in 3s
Import the current agent-native module as a fresh history.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-29 12:29:04 +00:00

86 lines
2.0 KiB
Go

package tools
import (
"fmt"
"os"
"path/filepath"
"strings"
)
// Resolve joins rel to root and rejects paths that escape the workspace,
// including symlink targets outside root. Missing files are allowed when their
// existing ancestors stay inside root.
func Resolve(root, rel string) (string, error) {
rootAbs, err := filepath.Abs(root)
if err != nil {
return "", err
}
rootAbs = filepath.Clean(rootAbs)
resolvedRoot, err := filepath.EvalSymlinks(rootAbs)
if err != nil {
return "", err
}
rel = strings.TrimSpace(rel)
if rel == "" {
rel = "."
}
if filepath.IsAbs(rel) {
inside, err := filepath.Rel(rootAbs, filepath.Clean(rel))
if err != nil || escapes(inside) {
return "", fmt.Errorf("path escapes workspace")
}
rel = inside
}
rel = filepath.Clean(rel)
if escapes(rel) {
return "", fmt.Errorf("path escapes workspace")
}
if rel == "." {
return resolvedRoot, nil
}
current := resolvedRoot
parts := strings.Split(rel, string(os.PathSeparator))
for i, part := range parts {
if part == "" || part == "." {
continue
}
if part == ".." {
return "", fmt.Errorf("path escapes workspace")
}
next := filepath.Join(current, part)
info, err := os.Lstat(next)
if os.IsNotExist(err) {
tail := filepath.Join(parts[i:]...)
return filepath.Join(current, tail), nil
}
if err != nil {
return "", err
}
if info.Mode()&os.ModeSymlink != 0 {
target, err := filepath.EvalSymlinks(next)
if err != nil {
return "", fmt.Errorf("path escapes workspace")
}
relTarget, err := filepath.Rel(resolvedRoot, target)
if err != nil || escapes(relTarget) {
return "", fmt.Errorf("path escapes workspace")
}
current = target
continue
}
current = next
}
relFinal, err := filepath.Rel(resolvedRoot, current)
if err != nil || escapes(relFinal) {
return "", fmt.Errorf("path escapes workspace")
}
return current, nil
}
func escapes(rel string) bool {
if rel == ".." {
return true
}
return strings.HasPrefix(rel, ".."+string(os.PathSeparator))
}