fix(exec): avoid URL false positives in workspace path guard

This commit is contained in:
westwind027 2026-02-25 13:50:55 +08:00
parent 0eade09809
commit b704ccdb1d
2 changed files with 34 additions and 3 deletions

View file

@ -289,10 +289,29 @@ func (t *ExecTool) guardCommand(command, cwd string) string {
return "" return ""
} }
pathPattern := regexp.MustCompile(`[A-Za-z]:\\[^\\\"']+|/[^\s\"']+`) //pathPattern := regexp.MustCompile(`[A-Za-z]:\\[^\\\"']+|/[^\s\"']+`)
matches := pathPattern.FindAllString(cmd, -1) //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) p, err := filepath.Abs(raw)
if err != nil { if err != nil {
continue continue

View file

@ -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)
}
}