Scope sandbox orphan probe to its own script (U5)

Why: The host-wide s.sh grep counted unrelated host scripts as sandbox escapes and failed the fixed combination even when its sandbox script had exited.

Evidence: SELF unrelated sentinel produced the exact original RED, while the scoped probe stayed GREEN under the same sentinel and detected its own positive-control script; full Backend Go tests passed. Receipts: /tmp/dependency-sandbox-host-collision-{red,green}-20261014-01a1345f.json and /tmp/dependency-backend-test-repair-all-go-green-20261014-01a1345f.json.

Impact: Test process identity check only; the sandbox process isolation implementation and failure condition for its own script are unchanged.

Refs: Backend PR #196; Dev PR #537; R8/R14
This commit is contained in:
2026-10-14 04:15:17 +00:00
parent af2702de8a
commit bada7ee40f
+78 -5
View File
@@ -13,6 +13,7 @@ import (
"os/exec"
"path/filepath"
"strings"
"syscall"
"testing"
"time"
)
@@ -87,6 +88,11 @@ func requireBwrap(t *testing.T) {
// sandboxRun 经 sandbox 后端执行一段 bash 脚本,返回合并输出与退出描述。
func sandboxRun(t *testing.T, script string, timeout time.Duration) (string, string) {
out, state, _ := sandboxRunWithPath(t, script, timeout)
return out, state
}
func sandboxRunWithPath(t *testing.T, script string, timeout time.Duration) (string, string, string) {
t.Helper()
dir := t.TempDir()
p := filepath.Join(dir, "s.sh")
@@ -117,7 +123,7 @@ func sandboxRun(t *testing.T, script string, timeout time.Duration) (string, str
if ctx.Err() == context.DeadlineExceeded {
state = "exit=timeout"
}
return out, state
return out, state, p
}
func TestSandboxHomeHidden(t *testing.T) {
@@ -207,17 +213,84 @@ func TestSandboxForkBombContained(t *testing.T) {
func TestSandboxOrphansDieWithNamespace(t *testing.T) {
requireBwrap(t)
out, state := sandboxRun(t, ":(){ :|:& };:; echo gone", 30*time.Second)
out, state, scriptPath := sandboxRunWithPath(t, ":(){ :|:& };:; echo gone", 30*time.Second)
if state != "exit=0" || !strings.Contains(out, "gone") {
t.Fatalf("state=%s out=%q", state, out)
}
time.Sleep(500 * time.Millisecond) // 给孤儿进程留存活窗口
hb, err := exec.Command("bash", "-c", "ps -eo args | grep -c '[s]\\.sh' || true").Output()
count, err := sandboxScriptProcessCount(scriptPath)
if err != nil {
t.Skipf("host ps probe failed: %v", err)
}
if strings.TrimSpace(string(hb)) != "0" {
t.Fatalf("orphaned bomb survived outside the namespace: %s", hb)
if count != 0 {
t.Fatalf("sandbox script still has %d host processes: %s", count, scriptPath)
}
}
func sandboxScriptProcessCount(scriptPath string) (int, error) {
out, err := exec.Command("ps", "-eo", "args").Output()
if err != nil {
return 0, err
}
count := 0
for _, args := range strings.Split(string(out), "\n") {
if strings.Contains(args, scriptPath) {
count++
}
}
return count, nil
}
func TestSandboxOrphanProbeScopesScript(t *testing.T) {
dir := t.TempDir()
actual := filepath.Join(dir, "actual", "s.sh")
unrelated := filepath.Join(dir, "unrelated", "s.sh")
for _, path := range []string{actual, unrelated} {
if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(path, []byte("sleep 10 & wait\n"), 0o600); err != nil {
t.Fatal(err)
}
}
start := func(path string) {
proc := exec.Command("bash", path)
proc.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
if err := proc.Start(); err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _ = syscall.Kill(-proc.Process.Pid, syscall.SIGKILL); _ = proc.Wait() })
}
start(unrelated)
for deadline := time.Now().Add(time.Second); ; {
count, err := sandboxScriptProcessCount(unrelated)
if err != nil {
t.Fatal(err)
}
if count > 0 {
break
}
if time.Now().After(deadline) {
t.Fatal("unrelated control script did not start")
}
time.Sleep(time.Millisecond)
}
if count, err := sandboxScriptProcessCount(actual); err != nil || count != 0 {
t.Fatalf("unrelated process counted as this sandbox script: count=%d err=%v", count, err)
}
start(actual)
for deadline := time.Now().Add(time.Second); ; {
count, err := sandboxScriptProcessCount(actual)
if err != nil {
t.Fatal(err)
}
if count > 0 {
break
}
if time.Now().After(deadline) {
t.Fatal("own sandbox script was not detected")
}
time.Sleep(time.Millisecond)
}
}