From c94de4c893f4549fd964a408fc178b22bd36ee88 Mon Sep 17 00:00:00 2001 From: wenjie Date: Wed, 11 Mar 2026 19:02:56 +0800 Subject: [PATCH] fix(exec): revalidate working_dir before command start --- pkg/tools/shell.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/pkg/tools/shell.go b/pkg/tools/shell.go index 08b6eef90..67e2ad257 100644 --- a/pkg/tools/shell.go +++ b/pkg/tools/shell.go @@ -219,6 +219,25 @@ func (t *ExecTool) Execute(ctx context.Context, args map[string]any) *ToolResult return ErrorResult(guardError) } + // Re-resolve symlinks immediately before execution to shrink the TOCTOU window + // between validation and cmd.Dir assignment. + if t.restrictToWorkspace && t.workingDir != "" && cwd != t.workingDir { + resolved, err := filepath.EvalSymlinks(cwd) + if err != nil { + return ErrorResult(fmt.Sprintf("Command blocked by safety guard (path resolution failed: %v)", err)) + } + absWorkspace, _ := filepath.Abs(t.workingDir) + wsResolved, _ := filepath.EvalSymlinks(absWorkspace) + if wsResolved == "" { + wsResolved = absWorkspace + } + rel, err := filepath.Rel(wsResolved, resolved) + if err != nil || !filepath.IsLocal(rel) { + return ErrorResult("Command blocked by safety guard (working directory escaped workspace)") + } + cwd = resolved + } + // timeout == 0 means no timeout var cmdCtx context.Context var cancel context.CancelFunc