mirror of
https://github.com/esengine/DeepSeek-Reasonix.git
synced 2026-11-07 10:45:59 -10:00
Merge pull request #7807 from chuiwenwei/fix/sandbox-available-probe
fix(sandbox): probe sandbox-exec usability instead of PATH presence
This commit is contained in:
3 files changed
+75
-5
No files matched your search
@@ -82,7 +82,7 @@ func UnavailableRemediation() string {
|
||||
case "linux":
|
||||
return "Install bubblewrap (`bwrap`) or set [sandbox] bash = \"off\" in config.toml / Settings -> Sandbox to restore pre-1.16 unconfined shell execution."
|
||||
case "darwin":
|
||||
return "Ensure `sandbox-exec` is available on PATH or set [sandbox] bash = \"off\" in config.toml / Settings -> Sandbox to restore pre-1.16 unconfined shell execution."
|
||||
return "Ensure `sandbox-exec` is installed and usable (the host must allow `sandbox_apply`), or set [sandbox] bash = \"off\" in config.toml / Settings -> Sandbox to restore pre-1.16 unconfined shell execution."
|
||||
case "windows":
|
||||
return "Windows does not currently provide a Reasonix OS-level Bash sandbox; the effective setting is fixed to \"off\" and shell commands run unconfined."
|
||||
default:
|
||||
@@ -97,7 +97,7 @@ func BackendUnavailableReason() string {
|
||||
case "linux":
|
||||
return "bubblewrap (bwrap) is unavailable on PATH"
|
||||
case "darwin":
|
||||
return "sandbox-exec is unavailable on PATH"
|
||||
return "sandbox-exec is missing from PATH or unusable (sandbox_apply is restricted)"
|
||||
case "windows":
|
||||
return "the AppContainer helper or required Windows sandbox APIs are unavailable"
|
||||
default:
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
package sandbox
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Command returns the argv to run `command` through sh, wrapped in sandbox-exec
|
||||
@@ -30,10 +33,35 @@ func CommandArgs(spec Spec, args []string) ([]string, bool) {
|
||||
return append([]string{"sandbox-exec", "-p", seatbeltProfile(spec)}, args...), true
|
||||
}
|
||||
|
||||
// Available reports whether sandbox-exec is on PATH (it ships with macOS).
|
||||
// sandboxExecUsability caches the probe result per resolved binary path, so
|
||||
// repeated Available() calls stay O(1) after the first check.
|
||||
var sandboxExecUsability sync.Map // resolved executable path -> bool
|
||||
|
||||
// usableSandboxExec distinguishes an installed sandbox-exec from a usable
|
||||
// Seatbelt backend. On restricted macOS hosts, sandbox-exec can be on PATH
|
||||
// while sandbox_apply fails with exit 71. Probe that operation directly with a
|
||||
// minimal profile, mirroring usableBwrap on Linux.
|
||||
func usableSandboxExec() bool {
|
||||
path, err := exec.LookPath("sandbox-exec")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if cached, ok := sandboxExecUsability.Load(path); ok {
|
||||
return cached.(bool)
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||
defer cancel()
|
||||
err = exec.CommandContext(ctx, path, "-p", "(version 1)(allow default)", "true").Run()
|
||||
usable := err == nil
|
||||
actual, _ := sandboxExecUsability.LoadOrStore(path, usable)
|
||||
return actual.(bool)
|
||||
}
|
||||
|
||||
// Available reports whether the OS sandbox backend can actually confine
|
||||
// processes. macOS probes sandbox-exec; Linux verifies bubblewrap can enter its
|
||||
// namespace (see seatbelt_other.go).
|
||||
func Available() bool {
|
||||
_, err := exec.LookPath("sandbox-exec")
|
||||
return err == nil
|
||||
return usableSandboxExec()
|
||||
}
|
||||
|
||||
// seatbeltProfile builds an SBPL profile that allows everything, then denies
|
||||
|
||||
@@ -295,3 +295,45 @@ func TestGoBuildUnderSandbox(t *testing.T) {
|
||||
t.Errorf("build output missing: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// fakeSandboxExec writes an executable named sandbox-exec into a fresh temp
|
||||
// dir, prepends that dir to PATH, and returns the binary's path. The probe
|
||||
// resolves the binary via PATH, so the fake shadows any real sandbox-exec on
|
||||
// the host.
|
||||
func fakeSandboxExec(t *testing.T, exitCode string) string {
|
||||
t.Helper()
|
||||
dir := t.TempDir()
|
||||
path := filepath.Join(dir, "sandbox-exec")
|
||||
if err := os.WriteFile(path, []byte("#!/bin/sh\nexit "+exitCode+"\n"), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Setenv("PATH", dir+string(os.PathListSeparator)+os.Getenv("PATH"))
|
||||
return path
|
||||
}
|
||||
|
||||
// TestAvailableFalseWhenSandboxExecUnusable covers the macOS 10.14+ case where
|
||||
// sandbox-exec is installed but sandbox_apply is refused (exit 71): the probe
|
||||
// must report unavailable so enforce mode fails loudly at boot instead of
|
||||
// silently on every command.
|
||||
func TestAvailableFalseWhenSandboxExecUnusable(t *testing.T) {
|
||||
path := fakeSandboxExec(t, "71")
|
||||
sandboxExecUsability.Delete(path) // the fake is fresh per test; re-probe it
|
||||
if Available() {
|
||||
t.Fatal("Available() = true, want false: sandbox-exec on PATH but unusable (exit 71)")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAvailableTrueWhenSandboxExecUsable(t *testing.T) {
|
||||
path := fakeSandboxExec(t, "0")
|
||||
sandboxExecUsability.Delete(path)
|
||||
if !Available() {
|
||||
t.Fatal("Available() = false, want true: working sandbox-exec")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAvailableFalseWhenSandboxExecMissing(t *testing.T) {
|
||||
t.Setenv("PATH", t.TempDir()) // no sandbox-exec anywhere on PATH
|
||||
if Available() {
|
||||
t.Fatal("Available() = true, want false: sandbox-exec not on PATH")
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user