返回 DeepSeek-Reasonix
seatbelt_other_test.go
根目录 / internal / sandbox / seatbelt_other_test.go
1 //go:build linux
2
3 package sandbox
4
5 import (
6 "os"
7 "path/filepath"
8 "reflect"
9 "testing"
10 )
11
12 func TestLinuxWriteDirsSkipsMissingDirs(t *testing.T) {
13 home := t.TempDir()
14 t.Setenv("HOME", home)
15 if err := os.Mkdir(filepath.Join(home, ".cache"), 0o755); err != nil {
16 t.Fatal(err)
17 }
18
19 got := linuxWriteDirs()
20 if !containsPath(got, filepath.Join(home, ".cache")) {
21 t.Fatalf("existing cache dir missing from linux write dirs: %v", got)
22 }
23 for _, missing := range []string{".cargo", ".npm", "go"} {
24 if containsPath(got, filepath.Join(home, missing)) {
25 t.Fatalf("missing dir %s should not be bound: %v", missing, got)
26 }
27 }
28 }
29
30 func TestBwrapExecutableMountArgsRevealsOnlyExactTemporaryExecutable(t *testing.T) {
31 got := bwrapExecutableMountArgs([]string{"/tmp/go-build123/b456/plugin.test", "-test.run=Helper"})
32 want := []string{
33 "--dir", "/tmp/go-build123",
34 "--dir", "/tmp/go-build123/b456",
35 "--ro-bind", "/tmp/go-build123/b456/plugin.test", "/tmp/go-build123/b456/plugin.test",
36 }
37 if !reflect.DeepEqual(got, want) {
38 t.Fatalf("temporary executable mount args = %v, want %v", got, want)
39 }
40 }
41
42 func TestBwrapExecutableMountArgsLeavesVisibleExecutableAlone(t *testing.T) {
43 if got := bwrapExecutableMountArgs([]string{"/usr/bin/node", "server.js"}); got != nil {
44 t.Fatalf("visible executable mount args = %v, want nil", got)
45 }
46 }
47
48 func TestBwrapArgsForArgsMountsTemporaryExecutableAfterMasks(t *testing.T) {
49 secretDir := t.TempDir()
50 argv := bwrapArgsForArgs(Spec{
51 ForbidReadRoots: []string{secretDir},
52 }, []string{"/tmp/go-build123/b456/plugin.test", "-test.run=Helper"})
53 mask := indexArgs(argv, "--tmpfs", secretDir)
54 mount := indexArgs(argv, "--ro-bind", "/tmp/go-build123/b456/plugin.test", "/tmp/go-build123/b456/plugin.test")
55 if mask < 0 || mount < 0 || mount < mask {
56 t.Fatalf("temporary executable must be mounted after masks: %v", argv)
57 }
58 }
59
60 func TestBwrapArgsBindsSessionTempAtTmp(t *testing.T) {
61 private := t.TempDir()
62 argv := bwrapArgs(Spec{
63 Mode: "enforce",
64 SessionTemp: private,
65 WriteRoots: []string{t.TempDir()},
66 }, Shell{Kind: ShellBash, Path: "bash"}, "true")
67 bind := indexArgs(argv, "--bind", private, "/tmp")
68 if bind < 0 {
69 t.Fatalf("expected --bind %s /tmp in %v", private, argv)
70 }
71 if indexArgs(argv, "--tmpfs", "/tmp") >= 0 {
72 t.Fatalf("session temp must not use tmpfs /tmp: %v", argv)
73 }
74 // Must not bind the host public temporary root as /tmp.
75 if host := os.TempDir(); host != private {
76 if indexArgs(argv, "--bind", host, "/tmp") >= 0 {
77 t.Fatalf("must not bind host temp %s at /tmp: %v", host, argv)
78 }
79 }
80 }
81
82 func TestBwrapArgsWithoutSessionTempKeepsTmpfs(t *testing.T) {
83 argv := bwrapArgs(Spec{Mode: "enforce"}, Shell{Kind: ShellBash, Path: "bash"}, "true")
84 if indexArgs(argv, "--tmpfs", "/tmp") < 0 {
85 t.Fatalf("independent sandbox should keep tmpfs /tmp: %v", argv)
86 }
87 }
88
89 func TestBwrapForbidReadArgsMasksFilesAndDirectories(t *testing.T) {
90 dir := t.TempDir()
91 nested := filepath.Join(dir, "nested")
92 if err := os.Mkdir(nested, 0o700); err != nil {
93 t.Fatal(err)
94 }
95 file := filepath.Join(t.TempDir(), "credentials.env")
96 if err := os.WriteFile(file, []byte("secret"), 0o600); err != nil {
97 t.Fatal(err)
98 }
99 missing := filepath.Join(dir, "missing")
100
101 got := bwrapForbidReadArgs([]string{dir, nested, file, file, missing})
102 want := []string{
103 "--tmpfs", dir,
104 "--ro-bind", "/dev/null", file,
105 }
106 if !reflect.DeepEqual(got, want) {
107 t.Fatalf("forbid-read mount args = %v, want %v", got, want)
108 }
109 }
110
111 func indexArgs(args []string, want ...string) int {
112 for i := 0; i+len(want) <= len(args); i++ {
113 if reflect.DeepEqual(args[i:i+len(want)], want) {
114 return i
115 }
116 }
117 return -1
118 }
119
120 func containsPath(paths []string, want string) bool {
121 absWant, err := filepath.Abs(want)
122 if err != nil {
123 return false
124 }
125 for _, p := range paths {
126 if p == absWant {
127 return true
128 }
129 }
130 return false
131 }
132
132 lines GO