Files
goclaw/internal/tools/sandbox_utils.go
T
viettranx a4a7a59b6a fix(sandbox): path isolation, DooD volume mounting, hints, and nginx DNS (#129, #136)
Address issues identified in PR #137 with a cleaner approach:

- Sandbox isolation: add SandboxCwd/ResolveSandboxPath helpers to map
  filesystem tool paths to agent-scoped container subdirectories,
  preventing cross-agent file access via read/write/edit/list tools
- DooD volume mounting: add resolveHostWorkspacePath with multi-strategy
  container ID detection (/proc/self/mountinfo, HOSTNAME, os.Hostname)
  and 5s timeout on docker inspect
- Sandbox hints: expand from 1 pattern (binary not found) to 6 patterns
  (permission denied, network disabled, read-only FS, missing file,
  resource limits) with MaybeFsBridgeHint for filesystem tools
- Nginx DNS: add Docker resolver (127.0.0.11) with dynamic upstream
  variable to handle backend container IP changes
- MCP args: switch from comma-separated to space-separated parsing
  with quote support for --flag="value with spaces" patterns
- Refactor: rename ExecTool.workingDir to workspace for consistency,
  extract sandbox.DefaultContainerWorkdir constant
2026-03-20 07:32:23 +07:00

46 lines
1.5 KiB
Go

package tools
import (
"context"
"fmt"
"path/filepath"
"strings"
)
// SandboxCwd maps the current effective workspace (from context) to its
// corresponding path inside the sandbox container. The sandbox mounts the
// global workspace root at containerBase (usually "/workspace"). This function
// computes the relative path from globalWorkspace to the context workspace
// and joins it with containerBase.
//
// Example: globalWorkspace="/app/workspace", ctx workspace="/app/workspace/agent-a/user-123"
// → returns "/workspace/agent-a/user-123"
func SandboxCwd(ctx context.Context, globalWorkspace, containerBase string) (string, error) {
ws := ToolWorkspaceFromCtx(ctx)
if ws == "" {
// No per-request workspace — fall back to container root.
return containerBase, nil
}
rel, err := filepath.Rel(globalWorkspace, ws)
if err != nil || strings.HasPrefix(filepath.Clean(rel), "..") {
return "", fmt.Errorf("workspace %q is outside global mount %q", ws, globalWorkspace)
}
if rel == "." {
return containerBase, nil
}
return filepath.Join(containerBase, rel), nil
}
// ResolveSandboxPath resolves a tool-provided path (relative or absolute)
// against the sandbox container CWD. If the path is relative, it is joined
// with containerCwd. Absolute paths are returned as-is (the sandbox
// filesystem already restricts access to the mounted volume).
func ResolveSandboxPath(path, containerCwd string) string {
if filepath.IsAbs(path) {
return path
}
return filepath.Join(containerCwd, path)
}