From 91ebe2fde7f7a574b5b72565d8094f0aa860c560 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Renzel?= Date: Sun, 1 Mar 2026 19:06:20 +0100 Subject: [PATCH] Fix: calling URLs with curl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Calling URLs via curl results in the error: “Command blocked by safety guard (path outside working dir)”. This commit fixes the problem. My changes to guardCommand ensure that the command is split into its individual parts (command + options/flags + arguments) and that each part is checked individually. In addition, web URLs are excluded from the check. --- pkg/tools/shell.go | 47 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/pkg/tools/shell.go b/pkg/tools/shell.go index 2fd22353f..2ea8a5539 100644 --- a/pkg/tools/shell.go +++ b/pkg/tools/shell.go @@ -5,11 +5,13 @@ import ( "context" "errors" "fmt" + "net/url" "os" "os/exec" "path/filepath" "regexp" "runtime" + "strconv" "strings" "time" @@ -75,6 +77,9 @@ var ( // absolutePathPattern matches absolute file paths in commands (Unix and Windows). absolutePathPattern = regexp.MustCompile(`[A-Za-z]:\\[^\\\"']+|/[^\s\"']+`) + + // split command in command + options/flags + arguments + splitPattern = regexp.MustCompile(`("[^"]*"|'[^']*'|[\S]+)+`) ) func NewExecTool(workingDir string, restrict bool) (*ExecTool, error) { @@ -286,30 +291,48 @@ func (t *ExecTool) guardCommand(command, cwd string) string { } if t.restrictToWorkspace { - if strings.Contains(cmd, "..\\") || strings.Contains(cmd, "../") { - return "Command blocked by safety guard (path traversal detected)" - } cwdPath, err := filepath.Abs(cwd) if err != nil { - return "" + cwdPath = "" } - matches := absolutePathPattern.FindAllString(cmd, -1) - - for _, raw := range matches { - p, err := filepath.Abs(raw) + parts := splitPattern.FindAllString(cmd, -1) + for _, part := range parts { + unquoted, err := strconv.Unquote(part) if err != nil { + unquoted = part + } + + u, err := url.ParseRequestURI(unquoted) + if err == nil && u.Scheme != "" && u.Host != "" { continue } - rel, err := filepath.Rel(cwdPath, p) - if err != nil { + if strings.Contains(part, "..\\") || strings.Contains(part, "../") { + return "Command blocked by safety guard (path traversal detected)" + } + + if cwdPath == "" { continue } - if strings.HasPrefix(rel, "..") { - return "Command blocked by safety guard (path outside working dir)" + matches := absolutePathPattern.FindAllString(part, -1) + + for _, raw := range matches { + p, err := filepath.Abs(raw) + if err != nil { + continue + } + + rel, err := filepath.Rel(cwdPath, p) + if err != nil { + continue + } + + if strings.HasPrefix(rel, "..") { + return "Command blocked by safety guard (path outside working dir)" + } } } }