package main import ( "fmt" "path/filepath" "strings" ) // resolveSafe joins p onto root, cleans it, and confirms the result stays // within root. Rejects absolute paths and anything that escapes via "..". func resolveSafe(root, p string) (string, error) { if p == "" { return "", fmt.Errorf("empty path") } if filepath.IsAbs(p) { return "", fmt.Errorf("absolute paths are not allowed: %s", p) } joined := filepath.Join(root, p) cleaned := filepath.Clean(joined) rel, err := filepath.Rel(root, cleaned) if err != nil { return "", fmt.Errorf("cannot resolve %q: %w", p, err) } if rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) { return "", fmt.Errorf("path escapes project root: %s", p) } return cleaned, nil }