fix: enforce workspace restriction in :edit command
- Export validatePath → ValidatePath for cross-package use - Rewrite resolveEditPath to validate paths via tools.ValidatePath, blocking absolute paths, symlink escape, and path traversal - Map ~ to workspace root (not $HOME) for consistency with cd - Return access denied error for paths outside workspace Security: previously :edit /etc/passwd or :edit ~/.ssh/id_rsa could read/write arbitrary files. Now all :edit paths are sandboxed to the agent workspace. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
001563ea74
commit
77f1759c3b
3 changed files with 20 additions and 13 deletions
|
|
@ -1353,7 +1353,7 @@ func (al *AgentLoop) executeCmdMode(ctx context.Context, agent *AgentInstance, c
|
|||
if workDir == "" {
|
||||
workDir = agent.Workspace
|
||||
}
|
||||
return al.handleEditCommand(content, workDir), nil
|
||||
return al.handleEditCommand(content, workDir, agent.Workspace), nil
|
||||
}
|
||||
|
||||
// Intercept interactive editors
|
||||
|
|
@ -1462,7 +1462,7 @@ func shortenHomePath(path string) string {
|
|||
// :edit <file> +<N> <text> → insert after line N
|
||||
// :edit <file> -<N> → delete line N
|
||||
// :edit <file> -m """<content>""" → write full content (create if needed)
|
||||
func (al *AgentLoop) handleEditCommand(content, workDir string) string {
|
||||
func (al *AgentLoop) handleEditCommand(content, workDir, workspace string) string {
|
||||
raw := strings.TrimSpace(strings.TrimPrefix(strings.TrimSpace(content), ":edit"))
|
||||
if raw == "" {
|
||||
return editUsage()
|
||||
|
|
@ -1479,7 +1479,10 @@ func (al *AgentLoop) handleEditCommand(content, workDir string) string {
|
|||
return editUsage()
|
||||
}
|
||||
|
||||
filename := resolveEditPath(parts[0], workDir)
|
||||
filename, err := resolveEditPath(parts[0], workDir, workspace)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Access denied: %s", err)
|
||||
}
|
||||
|
||||
// :edit <file> — show file content
|
||||
if len(parts) == 1 && !strings.Contains(raw, "\n") {
|
||||
|
|
@ -1501,15 +1504,19 @@ func (al *AgentLoop) handleEditCommand(content, workDir string) string {
|
|||
return editUsage()
|
||||
}
|
||||
|
||||
func resolveEditPath(name, workDir string) string {
|
||||
if strings.HasPrefix(name, "~/") {
|
||||
home, _ := os.UserHomeDir()
|
||||
return home + name[1:]
|
||||
func resolveEditPath(name, workDir, workspace string) (string, error) {
|
||||
// Treat ~ as workspace root (not $HOME)
|
||||
if name == "~" {
|
||||
name = "."
|
||||
} else if strings.HasPrefix(name, "~/") {
|
||||
name = name[2:]
|
||||
}
|
||||
if filepath.IsAbs(name) {
|
||||
return name
|
||||
// Resolve relative paths against workDir
|
||||
if !filepath.IsAbs(name) {
|
||||
name = filepath.Join(workDir, name)
|
||||
}
|
||||
return filepath.Join(workDir, name)
|
||||
// Validate against workspace (blocks absolute paths outside workspace, symlink escape, traversal)
|
||||
return tools.ValidatePath(name, workspace, true)
|
||||
}
|
||||
|
||||
func editUsage() string {
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
// validatePath ensures the given path is within the workspace if restrict is true.
|
||||
func validatePath(path, workspace string, restrict bool) (string, error) {
|
||||
// ValidatePath ensures the given path is within the workspace if restrict is true.
|
||||
func ValidatePath(path, workspace string, restrict bool) (string, error) {
|
||||
if workspace == "" {
|
||||
return path, fmt.Errorf("workspace is not defined")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ func (t *ExecTool) Execute(ctx context.Context, args map[string]any) *ToolResult
|
|||
cwd := t.workingDir
|
||||
if wd, ok := args["working_dir"].(string); ok && wd != "" {
|
||||
if t.restrictToWorkspace && t.workingDir != "" {
|
||||
resolvedWD, err := validatePath(wd, t.workingDir, true)
|
||||
resolvedWD, err := ValidatePath(wd, t.workingDir, true)
|
||||
if err != nil {
|
||||
return ErrorResult("Command blocked by safety guard (" + err.Error() + ")")
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue