package agentworkspace import ( "context" "errors" "io" "os" "os/exec" "path/filepath" "runtime" "strings" "testing" "iop/packages/go/agenttask" ) func TestConfinementPlatformFailsWithTypedUnavailableError(t *testing.T) { identity, err := platformConfinementRevision() if err != nil { if !errors.Is(err, ErrConfinementUnavailable) { t.Fatalf("platform error = %v, want ErrConfinementUnavailable", err) } return } if identity == "" { t.Fatal("available confinement returned an empty platform identity") } } func TestConfinementLinuxUsesMetadataCompleteMountNamespacePolicy(t *testing.T) { if runtime.GOOS != "linux" { t.Skip("Linux-only confinement policy") } identity, err := platformConfinementRevision() if err != nil { if errors.Is(err, ErrConfinementUnavailable) { return } t.Fatalf("platform confinement revision: %v", err) } if !strings.Contains(identity, "mount-namespace-metadata") || strings.Contains(identity, "landlock") { t.Fatalf("Linux confinement identity = %q, want metadata-complete mount namespace policy", identity) } } func TestConfinementDarwinUsesBoundWorkingDirectory(t *testing.T) { if runtime.GOOS != "darwin" { t.Skip("Darwin-only confinement command") } workingDirectory := t.TempDir() command, err := platformConfinementCommand( context.Background(), agenttask.ConfinementBinding{WorkingDir: workingDirectory}, confinementPolicy{WritableRoots: []string{workingDirectory}}, "/usr/bin/true", nil, ) if err != nil { t.Fatalf("platformConfinementCommand: %v", err) } if command.Dir != workingDirectory { t.Fatalf("confined command directory = %q", command.Dir) } } func TestConfinementProofRejectsTamperedBinding(t *testing.T) { baseRoot, localRoot := newWorkspaceFixture(t) writeFile(t, filepath.Join(baseRoot, "input.txt"), "base\n", 0o600) backend := newTestBackend(t, localRoot, baseRoot) prepared, err := backend.Prepare( context.Background(), testIsolationRequest("task", "task#1"), ) if err != nil { t.Fatalf("Prepare: %v", err) } proof, ok := prepared.Confinement.(*ConfinementProof) if !ok { t.Fatalf("confinement proof type = %T", prepared.Confinement) } expected := proof.Binding() expected.ProfileRevision = "profile-r2" if err := proof.Validate(expected); err == nil { t.Fatal("profile rebinding passed confinement validation") } tampered := *proof tampered.binding = proof.Binding() tampered.binding.BaseRoot = filepath.Dir(baseRoot) if _, err := tampered.Start( context.Background(), agenttask.ConfinementCommand{Name: "true"}, ); err == nil { t.Fatal("tampered proof produced a child command") } } func TestConfinementStartCreatesProofOwnedPipes(t *testing.T) { started, err := startConfinementCommand(exec.Command( "sh", "-c", `read line; printf 'stdout:%s\n' "$line"; printf 'stderr:%s\n' "$line" >&2`, )) if err != nil { t.Fatalf("startConfinementCommand: %v", err) } if started.Child() == nil || started.Stdin() == nil || started.Stdout() == nil || started.Stderr() == nil { t.Fatalf("started handle is incomplete: %#v", started) } type readResult struct { content string err error } stdoutResult := make(chan readResult, 1) stderrResult := make(chan readResult, 1) go func() { content, err := io.ReadAll(started.Stdout()) stdoutResult <- readResult{content: string(content), err: err} }() go func() { content, err := io.ReadAll(started.Stderr()) stderrResult <- readResult{content: string(content), err: err} }() if _, err := io.WriteString(started.Stdin(), "owned\n"); err != nil { t.Fatalf("write proof-owned stdin: %v", err) } if err := started.Stdin().Close(); err != nil { t.Fatalf("close proof-owned stdin: %v", err) } if err := started.Child().Wait(); err != nil { t.Fatalf("wait confined child: %v", err) } stdout := <-stdoutResult stderr := <-stderrResult if stdout.err != nil || stdout.content != "stdout:owned\n" { t.Fatalf("stdout = %q, %v", stdout.content, stdout.err) } if stderr.err != nil || stderr.content != "stderr:owned\n" { t.Fatalf("stderr = %q, %v", stderr.content, stderr.err) } if err := started.Abort(); err != nil { t.Fatalf("cleanup completed handle: %v", err) } } func TestConfinementStartCleansPipeAndProcessFailures(t *testing.T) { t.Run("pipe setup", func(t *testing.T) { var opened []*os.File calls := 0 factory := func() (*os.File, *os.File, error) { calls++ if calls == 3 { return nil, nil, errors.New("injected pipe failure") } reader, writer, err := os.Pipe() if err == nil { opened = append(opened, reader, writer) } return reader, writer, err } if _, err := startConfinementCommandWithPipes( exec.Command("true"), factory, ); err == nil || !strings.Contains(err.Error(), "stderr pipe") { t.Fatalf("pipe setup error = %v", err) } assertClosedFiles(t, opened) }) t.Run("process start", func(t *testing.T) { var opened []*os.File factory := func() (*os.File, *os.File, error) { reader, writer, err := os.Pipe() if err == nil { opened = append(opened, reader, writer) } return reader, writer, err } missing := filepath.Join(t.TempDir(), "missing-command") if _, err := startConfinementCommandWithPipes( exec.Command(missing), factory, ); err == nil || !strings.Contains(err.Error(), "start confined child") { t.Fatalf("process start error = %v", err) } assertClosedFiles(t, opened) }) } func TestConfinementAbortClosesPipesAndReapsChild(t *testing.T) { started, err := startConfinementCommand(exec.Command("sleep", "5")) if err != nil { t.Fatalf("startConfinementCommand: %v", err) } stdin := started.Stdin() stdout := started.Stdout() stderr := started.Stderr() if err := started.Abort(); err != nil { t.Fatalf("Abort: %v", err) } if started.Child().ProcessState == nil { t.Fatal("Abort did not reap the child") } if _, err := stdin.Write([]byte("leak")); err == nil { t.Fatal("Abort left stdin open") } if _, err := stdout.Read(make([]byte, 1)); err == nil { t.Fatal("Abort left stdout open") } if _, err := stderr.Read(make([]byte, 1)); err == nil { t.Fatal("Abort left stderr open") } if err := started.Abort(); err != nil { t.Fatalf("second Abort: %v", err) } } func assertClosedFiles(t *testing.T, files []*os.File) { t.Helper() for _, file := range files { if _, err := file.Stat(); err == nil { t.Fatalf("pipe endpoint %d remained open", file.Fd()) } } }