mirror of
https://github.com/YspCoder/clawgo.git
synced 2026-05-18 03:37:33 +08:00
fix filepath
This commit is contained in:
@@ -157,9 +157,9 @@ func NewAgentLoop(cfg *config.Config, msgBus *bus.MessageBus, provider providers
|
|||||||
})
|
})
|
||||||
|
|
||||||
toolsRegistry := tools.NewToolRegistry()
|
toolsRegistry := tools.NewToolRegistry()
|
||||||
toolsRegistry.Register(tools.NewReadFileTool(""))
|
toolsRegistry.Register(tools.NewReadFileTool(workspace))
|
||||||
toolsRegistry.Register(tools.NewWriteFileTool(""))
|
toolsRegistry.Register(tools.NewWriteFileTool(workspace))
|
||||||
toolsRegistry.Register(tools.NewListDirTool(""))
|
toolsRegistry.Register(tools.NewListDirTool(workspace))
|
||||||
toolsRegistry.Register(tools.NewExecTool(cfg.Tools.Shell, workspace))
|
toolsRegistry.Register(tools.NewExecTool(cfg.Tools.Shell, workspace))
|
||||||
|
|
||||||
if cs != nil {
|
if cs != nil {
|
||||||
@@ -196,7 +196,7 @@ func NewAgentLoop(cfg *config.Config, msgBus *bus.MessageBus, provider providers
|
|||||||
toolsRegistry.Register(tools.NewPipelineDispatchTool(orchestrator, subagentManager))
|
toolsRegistry.Register(tools.NewPipelineDispatchTool(orchestrator, subagentManager))
|
||||||
|
|
||||||
// Register edit file tool
|
// Register edit file tool
|
||||||
editFileTool := tools.NewEditFileTool("")
|
editFileTool := tools.NewEditFileTool(workspace)
|
||||||
toolsRegistry.Register(editFileTool)
|
toolsRegistry.Register(editFileTool)
|
||||||
|
|
||||||
// Register memory search tool
|
// Register memory search tool
|
||||||
|
|||||||
@@ -8,6 +8,20 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func resolveToolPath(baseDir, path string) (string, error) {
|
||||||
|
if filepath.IsAbs(path) {
|
||||||
|
return filepath.Clean(path), nil
|
||||||
|
}
|
||||||
|
if baseDir != "" {
|
||||||
|
return filepath.Clean(filepath.Join(baseDir, path)), nil
|
||||||
|
}
|
||||||
|
abs, err := filepath.Abs(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to resolve path: %w", err)
|
||||||
|
}
|
||||||
|
return abs, nil
|
||||||
|
}
|
||||||
|
|
||||||
// ReadFileTool reads the contents of a file.
|
// ReadFileTool reads the contents of a file.
|
||||||
type ReadFileTool struct {
|
type ReadFileTool struct {
|
||||||
allowedDir string
|
allowedDir string
|
||||||
@@ -52,22 +66,9 @@ func (t *ReadFileTool) Execute(ctx context.Context, args map[string]interface{})
|
|||||||
return "", fmt.Errorf("path is required")
|
return "", fmt.Errorf("path is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
resolvedPath := path
|
resolvedPath, err := resolveToolPath(t.allowedDir, path)
|
||||||
if filepath.IsAbs(path) {
|
if err != nil {
|
||||||
resolvedPath = filepath.Clean(path)
|
return "", err
|
||||||
} else {
|
|
||||||
abs, err := filepath.Abs(path)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("failed to resolve path: %w", err)
|
|
||||||
}
|
|
||||||
resolvedPath = abs
|
|
||||||
}
|
|
||||||
|
|
||||||
if t.allowedDir != "" {
|
|
||||||
allowedAbs, _ := filepath.Abs(t.allowedDir)
|
|
||||||
if !strings.HasPrefix(resolvedPath, allowedAbs) {
|
|
||||||
return "", fmt.Errorf("path %s is outside allowed directory", path)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
f, err := os.Open(resolvedPath)
|
f, err := os.Open(resolvedPath)
|
||||||
@@ -149,22 +150,9 @@ func (t *WriteFileTool) Execute(ctx context.Context, args map[string]interface{}
|
|||||||
return "", fmt.Errorf("content is required")
|
return "", fmt.Errorf("content is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
resolvedPath := path
|
resolvedPath, err := resolveToolPath(t.allowedDir, path)
|
||||||
if filepath.IsAbs(path) {
|
if err != nil {
|
||||||
resolvedPath = filepath.Clean(path)
|
return "", err
|
||||||
} else {
|
|
||||||
abs, err := filepath.Abs(path)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("failed to resolve path: %w", err)
|
|
||||||
}
|
|
||||||
resolvedPath = abs
|
|
||||||
}
|
|
||||||
|
|
||||||
if t.allowedDir != "" {
|
|
||||||
allowedAbs, _ := filepath.Abs(t.allowedDir)
|
|
||||||
if !strings.HasPrefix(resolvedPath, allowedAbs) {
|
|
||||||
return "", fmt.Errorf("path %s is outside allowed directory", path)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := os.WriteFile(resolvedPath, []byte(content), 0644); err != nil {
|
if err := os.WriteFile(resolvedPath, []byte(content), 0644); err != nil {
|
||||||
@@ -216,22 +204,9 @@ func (t *ListDirTool) Execute(ctx context.Context, args map[string]interface{})
|
|||||||
|
|
||||||
recursive, _ := args["recursive"].(bool)
|
recursive, _ := args["recursive"].(bool)
|
||||||
|
|
||||||
resolvedPath := path
|
resolvedPath, err := resolveToolPath(t.allowedDir, path)
|
||||||
if filepath.IsAbs(path) {
|
if err != nil {
|
||||||
resolvedPath = filepath.Clean(path)
|
return "", err
|
||||||
} else {
|
|
||||||
abs, err := filepath.Abs(path)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("failed to resolve path: %w", err)
|
|
||||||
}
|
|
||||||
resolvedPath = abs
|
|
||||||
}
|
|
||||||
|
|
||||||
if t.allowedDir != "" {
|
|
||||||
allowedAbs, _ := filepath.Abs(t.allowedDir)
|
|
||||||
if !strings.HasPrefix(resolvedPath, allowedAbs) {
|
|
||||||
return "", fmt.Errorf("path %s is outside allowed directory", path)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var results []string
|
var results []string
|
||||||
@@ -330,22 +305,9 @@ func (t *EditFileTool) Execute(ctx context.Context, args map[string]interface{})
|
|||||||
return "", fmt.Errorf("new_text is required")
|
return "", fmt.Errorf("new_text is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
resolvedPath := path
|
resolvedPath, err := resolveToolPath(t.allowedDir, path)
|
||||||
if filepath.IsAbs(path) {
|
if err != nil {
|
||||||
resolvedPath = filepath.Clean(path)
|
return "", err
|
||||||
} else {
|
|
||||||
abs, err := filepath.Abs(path)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("failed to resolve path: %w", err)
|
|
||||||
}
|
|
||||||
resolvedPath = abs
|
|
||||||
}
|
|
||||||
|
|
||||||
if t.allowedDir != "" {
|
|
||||||
allowedAbs, _ := filepath.Abs(t.allowedDir)
|
|
||||||
if !strings.HasPrefix(resolvedPath, allowedAbs) {
|
|
||||||
return "", fmt.Errorf("path %s is outside allowed directory", path)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
content, err := os.ReadFile(resolvedPath)
|
content, err := os.ReadFile(resolvedPath)
|
||||||
|
|||||||
54
pkg/tools/filesystem_test.go
Normal file
54
pkg/tools/filesystem_test.go
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package tools
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestReadFileToolResolvesRelativePathFromAllowedDir(t *testing.T) {
|
||||||
|
workspace := t.TempDir()
|
||||||
|
targetPath := filepath.Join(workspace, "cmd", "clawgo", "main.go")
|
||||||
|
if err := os.MkdirAll(filepath.Dir(targetPath), 0755); err != nil {
|
||||||
|
t.Fatalf("mkdir failed: %v", err)
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(targetPath, []byte("package main"), 0644); err != nil {
|
||||||
|
t.Fatalf("write failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tool := NewReadFileTool(workspace)
|
||||||
|
out, err := tool.Execute(context.Background(), map[string]interface{}{
|
||||||
|
"path": "cmd/clawgo/main.go",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if out != "package main" {
|
||||||
|
t.Fatalf("unexpected output: %q", out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadFileToolAllowsParentTraversalWhenPermitted(t *testing.T) {
|
||||||
|
workspace := t.TempDir()
|
||||||
|
parentFile := filepath.Join(filepath.Dir(workspace), "outside.txt")
|
||||||
|
if err := os.WriteFile(parentFile, []byte("outside"), 0644); err != nil {
|
||||||
|
t.Fatalf("write failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tool := NewReadFileTool(workspace)
|
||||||
|
relPath, err := filepath.Rel(workspace, parentFile)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("rel failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := tool.Execute(context.Background(), map[string]interface{}{
|
||||||
|
"path": relPath,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if out != "outside" {
|
||||||
|
t.Fatalf("unexpected output: %q", out)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user