返回 DeepSeek-Reasonix
writable_roots_test.go
根目录 / internal / sandbox / writable_roots_test.go
1 package sandbox
2
3 import (
4 "context"
5 "os"
6 "path/filepath"
7 "runtime"
8 "sync"
9 "testing"
10 )
11
12 func TestWritableRootSetSnapshotAndMissing(t *testing.T) {
13 base := t.TempDir()
14 extra := filepath.Join(t.TempDir(), "extra")
15 set := NewWritableRootSet([]string{base})
16 if !set.Covers(filepath.Join(base, "src")) {
17 t.Fatal("workspace child should be covered")
18 }
19 if set.Covers(extra) {
20 t.Fatal("unrelated dir should not be covered")
21 }
22 missing := set.Missing([]string{extra, filepath.Join(base, "pkg")})
23 if len(missing) != 1 || !PathWithin(canonicalDir(extra), missing[0]) {
24 t.Fatalf("Missing = %v, want [%s]", missing, extra)
25 }
26 set.GrantSession([]string{extra})
27 if !set.Covers(filepath.Join(extra, "bin")) {
28 t.Fatal("session grant should cover children")
29 }
30 if len(set.Missing([]string{extra})) != 0 {
31 t.Fatal("granted dir should not be missing")
32 }
33 set.ClearSession()
34 if set.Covers(extra) {
35 t.Fatal("ClearSession should drop session grants")
36 }
37 }
38
39 func TestWritableRootSetRevokesOnlyExactSessionRoot(t *testing.T) {
40 root := t.TempDir()
41 one := filepath.Join(root, "one")
42 two := filepath.Join(root, "two")
43 for _, dir := range []string{one, two} {
44 if err := os.MkdirAll(dir, 0o755); err != nil {
45 t.Fatal(err)
46 }
47 }
48 set := NewWritableRootSet(nil)
49 set.GrantSession([]string{one, two})
50 if !set.RevokeSession(one) {
51 t.Fatal("expected exact session root to be revoked")
52 }
53 if set.Covers(one) {
54 t.Fatal("revoked root remains writable")
55 }
56 if !set.Covers(two) {
57 t.Fatal("revoking one root removed an unrelated grant")
58 }
59 if set.RevokeSession(one) {
60 t.Fatal("second revocation should report no change")
61 }
62 }
63
64 func TestWritableRootSetReplaceBaselineKeepsSession(t *testing.T) {
65 a := t.TempDir()
66 b := t.TempDir()
67 sess := t.TempDir()
68 set := NewWritableRootSet([]string{a})
69 set.GrantSession([]string{sess})
70 set.ReplaceBaseline([]string{b})
71 if set.Covers(a) {
72 t.Fatal("old baseline should be gone")
73 }
74 if !set.Covers(b) || !set.Covers(sess) {
75 t.Fatal("new baseline and session grant should remain")
76 }
77 }
78
79 func TestWritableRootSetVerifiedBaselineSurvivesSessionClear(t *testing.T) {
80 base := t.TempDir()
81 project := t.TempDir()
82 set := NewWritableRootSet([]string{base})
83 set.GrantVerifiedBaseline([]string{canonicalDir(project)})
84 set.ClearSession()
85 if !set.Covers(project) {
86 t.Fatal("project grant should remain in the baseline after session clear")
87 }
88 }
89
90 func TestWritableRootSetPerCallDoesNotLeak(t *testing.T) {
91 base := t.TempDir()
92 once := canonicalDir(t.TempDir())
93 set := NewWritableRootSet([]string{base})
94 ctx := WithPerCallWriteRoots(context.Background(), []string{once})
95 if got := set.Effective(ctx); !containsRoot(got, once) {
96 t.Fatalf("Effective should include per-call root, got %v", got)
97 }
98 if set.Covers(once) {
99 t.Fatal("per-call root must not enter the session snapshot")
100 }
101 }
102
103 func TestWritableRootSetConcurrentReads(t *testing.T) {
104 set := NewWritableRootSet([]string{t.TempDir()})
105 var wg sync.WaitGroup
106 for range 8 {
107 wg.Go(func() {
108 _ = set.Snapshot()
109 set.GrantSession([]string{t.TempDir()})
110 _ = set.Effective(context.Background())
111 })
112 }
113 wg.Wait()
114 }
115
116 func TestIntersectWriteRoots(t *testing.T) {
117 root := t.TempDir()
118 child := filepath.Join(root, "src")
119 other := t.TempDir()
120 got := IntersectWriteRoots([]string{root}, []string{child, other})
121 if len(got) != 1 || !PathWithin(canonicalDir(child), got[0]) {
122 t.Fatalf("IntersectWriteRoots = %v, want [%s]", got, child)
123 }
124 if len(IntersectWriteRoots([]string{root}, []string{other})) != 0 {
125 t.Fatal("disjoint roots should intersect to empty")
126 }
127 }
128
129 func TestCloneRestricted(t *testing.T) {
130 root := t.TempDir()
131 child := filepath.Join(root, "pkg")
132 sess := t.TempDir()
133 set := NewWritableRootSet([]string{root})
134 set.GrantSession([]string{sess})
135 restricted := set.CloneRestricted([]string{child})
136 if !restricted.Covers(child) {
137 t.Fatal("restricted view should keep the intersection")
138 }
139 if restricted.Covers(sess) {
140 t.Fatal("write_paths intersection must drop unrelated session grants")
141 }
142 inherited := set.CloneRestricted(nil)
143 if !inherited.Covers(sess) || !inherited.Covers(root) {
144 t.Fatal("empty cap should copy the current snapshot")
145 }
146 }
147
148 func TestStableWriteRootsDropsRetargetedIdentity(t *testing.T) {
149 if runtime.GOOS == "windows" {
150 t.Skip("symlink creation is not guaranteed on Windows runners")
151 }
152 root := canonicalDir(t.TempDir())
153 approved := filepath.Join(root, "approved")
154 redirected := filepath.Join(root, "redirected")
155 if err := os.MkdirAll(approved, 0o755); err != nil {
156 t.Fatal(err)
157 }
158 if err := os.MkdirAll(redirected, 0o755); err != nil {
159 t.Fatal(err)
160 }
161 if err := os.Remove(approved); err != nil {
162 t.Fatal(err)
163 }
164 if err := os.Symlink(redirected, approved); err != nil {
165 t.Fatal(err)
166 }
167 set := newVerifiedWritableRootSet([]string{approved})
168 if got := set.EffectiveSandboxRoots(context.Background()); len(got) != 0 {
169 t.Fatalf("retargeted root must fail closed, got %v", got)
170 }
171 if set.Covers(approved) {
172 t.Fatal("retargeted root must no longer count as covered")
173 }
174 if got := set.Missing([]string{approved}); len(got) != 1 {
175 t.Fatalf("retargeted root must be eligible for re-approval, got %v", got)
176 }
177 }
178
179 func containsRoot(roots []string, want string) bool {
180 want = canonicalDir(want)
181 for _, root := range roots {
182 if PathWithin(root, want) {
183 return true
184 }
185 }
186 return false
187 }
188
188 lines GO