diff --git a/pkg/isolation/platform_linux.go b/pkg/isolation/platform_linux.go index 1b0767fa2..429a6f6c6 100644 --- a/pkg/isolation/platform_linux.go +++ b/pkg/isolation/platform_linux.go @@ -44,9 +44,9 @@ func applyPlatformIsolation(cmd *exec.Cmd, isolation config.IsolationConfig, roo originalArgs := append([]string{}, cmd.Args...) originalDir := cmd.Dir - // Start from the configured mount plan, then add the executable, its resolved - // path, the working directory, and any absolute path arguments the process may - // need at runtime. + // Start from the configured mount plan, then add only the executable, its + // resolved path, and the working directory. Any additional host paths must be + // exposed explicitly via config instead of being inferred from argv. plan := BuildLinuxMountPlan(root, isolation.ExposePaths) plan = ensureLinuxMountRule(plan, originalPath, originalPath, "ro") plan = ensureLinuxMountRule(plan, filepath.Dir(originalPath), filepath.Dir(originalPath), "ro") @@ -60,11 +60,6 @@ func applyPlatformIsolation(cmd *exec.Cmd, isolation config.IsolationConfig, roo plan = ensureLinuxMountRule(plan, resolved, resolved, "rw") } } - for _, arg := range originalArgs[1:] { - if filepath.IsAbs(arg) { - plan = ensureLinuxPathForArgument(plan, arg) - } - } logger.DebugCF("isolation", "linux isolation mount plan", map[string]any{ "root": root, @@ -166,24 +161,3 @@ func linuxBindFlag(rule MountRule) (string, error) { } return "--ro-bind", nil } - -// ensureLinuxPathForArgument exposes absolute-path arguments conservatively so -// common CLI flags that point at files or directories keep working in the -// isolated filesystem view. -func ensureLinuxPathForArgument(plan []MountRule, arg string) []MountRule { - clean := filepath.Clean(arg) - if info, err := os.Stat(clean); err == nil { - if info.IsDir() { - return ensureLinuxMountRule(plan, clean, clean, "rw") - } - return ensureLinuxMountRule(plan, filepath.Dir(clean), filepath.Dir(clean), "rw") - } - parent := filepath.Dir(clean) - if parent == "." || parent == "/" { - return plan - } - if _, err := os.Stat(parent); err == nil { - return ensureLinuxMountRule(plan, parent, parent, "rw") - } - return plan -} diff --git a/pkg/isolation/platform_linux_test.go b/pkg/isolation/platform_linux_test.go index c9a14e4eb..12a90c0ba 100644 --- a/pkg/isolation/platform_linux_test.go +++ b/pkg/isolation/platform_linux_test.go @@ -30,6 +30,8 @@ func TestBuildLinuxBwrapArgs_IncludesNamespaceFlagsAndExec(t *testing.T) { hasExec := false for i := range args { switch args[i] { + case "--unshare-net": + hasNet = true case "--unshare-ipc": hasIPC = true case "--": diff --git a/pkg/isolation/runtime.go b/pkg/isolation/runtime.go index 4e4085b0a..1124d9871 100644 --- a/pkg/isolation/runtime.go +++ b/pkg/isolation/runtime.go @@ -294,7 +294,11 @@ func BuildWindowsAccessRules(root string, overrides []config.ExposePath) []Acces // IsSupported reports whether the current platform has an implemented isolation // backend. func IsSupported() bool { - switch runtime.GOOS { + return isSupportedOn(runtime.GOOS) +} + +func isSupportedOn(goos string) bool { + switch goos { case "linux", "windows": return true default: @@ -309,6 +313,9 @@ func Preflight() error { if !isolation.Enabled { return nil } + if !IsSupported() { + return fmt.Errorf("subprocess isolation is not supported on %s", runtime.GOOS) + } root, err := ResolveInstanceRoot() if err != nil { return err diff --git a/pkg/isolation/runtime_test.go b/pkg/isolation/runtime_test.go index b8cc816c3..49193554c 100644 --- a/pkg/isolation/runtime_test.go +++ b/pkg/isolation/runtime_test.go @@ -1,6 +1,7 @@ package isolation import ( + "os" "os/exec" "path/filepath" "runtime" @@ -25,17 +26,28 @@ func TestPrepareInstanceRoot_CreatesDirectories(t *testing.T) { if err := PrepareInstanceRoot(root); err != nil { t.Fatalf("PrepareInstanceRoot() error = %v", err) } - for _, dir := range []string{ - root, - filepath.Join(root, "workspace"), - filepath.Join(root, "skills"), - filepath.Join(root, "logs"), - filepath.Join(root, "cache"), - filepath.Join(root, "state"), - filepath.Join(root, "runtime-user-env"), - } { - if _, err := filepath.Abs(dir); err != nil { - t.Fatalf("filepath.Abs(%q): %v", dir, err) + for _, dir := range InstanceDirs(root) { + if info, err := os.Stat(dir); err != nil { + t.Fatalf("os.Stat(%q): %v", dir, err) + } else if !info.IsDir() { + t.Fatalf("%q is not a directory", dir) + } + } +} + +func TestIsSupportedOn(t *testing.T) { + tests := []struct { + goos string + want bool + }{ + {goos: "linux", want: true}, + {goos: "windows", want: true}, + {goos: "darwin", want: false}, + {goos: "freebsd", want: false}, + } + for _, tt := range tests { + if got := isSupportedOn(tt.goos); got != tt.want { + t.Fatalf("isSupportedOn(%q) = %v, want %v", tt.goos, got, tt.want) } } }