From b704ccdb1d666ac9e5ccd59a8a22ebdabd49d633 Mon Sep 17 00:00:00 2001 From: westwind027 <220070570@seu.edu.cn> Date: Wed, 25 Feb 2026 13:50:55 +0800 Subject: [PATCH] fix(exec): avoid URL false positives in workspace path guard --- pkg/tools/shell.go | 25 ++++++++++++++++++++++--- pkg/tools/shell_test.go | 12 ++++++++++++ 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/pkg/tools/shell.go b/pkg/tools/shell.go index 6883172cd..c28701655 100644 --- a/pkg/tools/shell.go +++ b/pkg/tools/shell.go @@ -289,10 +289,29 @@ func (t *ExecTool) guardCommand(command, cwd string) string { return "" } - pathPattern := regexp.MustCompile(`[A-Za-z]:\\[^\\\"']+|/[^\s\"']+`) - matches := pathPattern.FindAllString(cmd, -1) + //pathPattern := regexp.MustCompile(`[A-Za-z]:\\[^\\\"']+|/[^\s\"']+`) + //matches := pathPattern.FindAllString(cmd, -1) + + // Strip URLs before path checking so they don't get misidentified as file paths. + // Handle both scheme URLs (https://a/b) and domain URLs without scheme (example.com/a). + urlPattern := regexp.MustCompile(`[a-zA-Z][a-zA-Z0-9+.-]*://[^\s"']+`) + stripped := urlPattern.ReplaceAllString(cmd, "") + domainURLPattern := regexp.MustCompile(`\b(?:[A-Za-z0-9-]+\.)+[A-Za-z]{2,}(?::\d+)?/[^\s"']*`) + stripped = domainURLPattern.ReplaceAllString(stripped, "") + + pathPattern := regexp.MustCompile(`[A-Za-z]:\\[^\\\"']+|/[^\s\"']+`) + matches := pathPattern.FindAllStringIndex(stripped, -1) + + for _, match := range matches { + raw := stripped[match[0]:match[1]] + + if match[0] > 0 { + prev := stripped[match[0]-1] + if prev != ' ' && prev != '\t' && prev != '\n' && prev != '"' && prev != '\'' && prev != '=' && prev != '(' { + continue + } + } - for _, raw := range matches { p, err := filepath.Abs(raw) if err != nil { continue diff --git a/pkg/tools/shell_test.go b/pkg/tools/shell_test.go index 6d35815e8..fcb05356d 100644 --- a/pkg/tools/shell_test.go +++ b/pkg/tools/shell_test.go @@ -272,3 +272,15 @@ func TestShellTool_RestrictToWorkspace(t *testing.T) { ) } } + +// TestShellTool_RestrictToWorkspace_AllowsDomainURLWithoutScheme verifies +// URL-like domain paths (without scheme) are not treated as local absolute paths. +func TestShellTool_RestrictToWorkspace_AllowsDomainURLWithoutScheme(t *testing.T) { + tmpDir := t.TempDir() + tool := NewExecTool(tmpDir, true) + + guardError := tool.guardCommand(`curl -s "wttr.in/Nanjing?format=3"`, tmpDir) + if guardError != "" { + t.Fatalf("expected domain URL to pass safety guard, got: %s", guardError) + } +}