Skip to content

Commit af0a603

Browse files
drpedapaticlaude
andcommitted
fix(tools): allow paths within routed workspace in exec guard
The exec safety guard was blocking commands containing absolute paths to routed workspaces (e.g., /home/ernie/Dropbox/sciclaw/project) when the shared workspace was different (e.g., ~/sciclaw). The guard only checked if paths were within: 1. The current working directory (cwd) 2. The shared workspace But not the tool's configured workspace (t.workingDir), which is set to the routed workspace for channel-specific routing. This caused legitimate commands like: cd /routed/workspace/subdir && git commit -m "message" to be blocked with "path outside working dir". Fix: Also add t.workingDir to allowedRoots when it differs from both cwd and sharedWorkspace. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 4c333d1 commit af0a603

File tree

2 files changed

+33
-0
lines changed

2 files changed

+33
-0
lines changed

pkg/tools/shell.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,12 @@ func (t *ExecTool) guardCommand(command, cwd string) string {
686686
if sharedRoot != "" && !isWithinWorkspace(cwdPath, sharedRoot) {
687687
allowedRoots = append(allowedRoots, guardRoot{path: sharedRoot, readOnly: t.sharedWorkspaceReadOnly})
688688
}
689+
// Also allow paths within the tool's configured workspace (for routed workspaces
690+
// that differ from the shared workspace).
691+
wsRoot := absCleanPath(t.workingDir)
692+
if wsRoot != "" && wsRoot != cwdPath && wsRoot != sharedRoot && !isWithinWorkspace(wsRoot, sharedRoot) {
693+
allowedRoots = append(allowedRoots, guardRoot{path: wsRoot, readOnly: false})
694+
}
689695

690696
for _, raw := range matches {
691697
p, err := filepath.Abs(raw)

pkg/tools/shell_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package tools
22

33
import (
44
"context"
5+
"fmt"
56
"os"
67
"path/filepath"
78
"strconv"
@@ -455,6 +456,32 @@ func TestShellTool_AllowsDirectPubMedCLI(t *testing.T) {
455456
}
456457
}
457458

459+
func TestShellTool_AllowsRoutedWorkspacePathInCommand(t *testing.T) {
460+
// Simulate a routed workspace that differs from the shared workspace.
461+
// This tests the bug where commands containing absolute paths to the routed
462+
// workspace were blocked because only sharedWorkspace was in allowedRoots.
463+
routedWorkspace := t.TempDir()
464+
sharedWorkspace := t.TempDir()
465+
subdir := filepath.Join(routedWorkspace, "vecflow")
466+
os.MkdirAll(subdir, 0755)
467+
468+
tool := NewExecTool(routedWorkspace, false)
469+
tool.SetRestrictToWorkspace(true)
470+
tool.SetSharedWorkspacePolicy(sharedWorkspace, false)
471+
472+
// Command that contains an absolute path to the routed workspace
473+
cmd := fmt.Sprintf("cd %s && git status", subdir)
474+
if blocked := tool.guardCommand(cmd, routedWorkspace); blocked != "" {
475+
t.Fatalf("expected routed workspace path to pass guard, got: %q", blocked)
476+
}
477+
478+
// Also test with working_dir pointing to routed workspace
479+
cmd2 := fmt.Sprintf("cat %s/file.txt", subdir)
480+
if blocked := tool.guardCommand(cmd2, routedWorkspace); blocked != "" {
481+
t.Fatalf("expected routed workspace subdir path to pass guard, got: %q", blocked)
482+
}
483+
}
484+
458485
func TestShouldApplyNIHPandocTemplate(t *testing.T) {
459486
cases := []struct {
460487
name string

0 commit comments

Comments
 (0)