返回 DeepSeek-Reasonix
confinedread_test.go
根目录 / internal / fileutil / confinedread_test.go
1 package fileutil
2
3 import (
4 "io"
5 "os"
6 "path/filepath"
7 "strings"
8 "testing"
9 )
10
11 func TestOpenFileBeneathRejectsWorkspaceEscape(t *testing.T) {
12 root := t.TempDir()
13 outside := filepath.Join(t.TempDir(), "secret.txt")
14 if err := os.WriteFile(outside, []byte("secret"), 0o600); err != nil {
15 t.Fatal(err)
16 }
17 link := filepath.Join(root, "escape.txt")
18 if err := os.Symlink(outside, link); err != nil {
19 t.Skipf("symlink unavailable: %v", err)
20 }
21
22 file, err := OpenFileBeneath(root, "escape.txt")
23 if err == nil {
24 file.Close()
25 t.Fatal("outside-workspace symlink was accepted")
26 }
27 if !strings.Contains(err.Error(), "outside workspace") {
28 t.Fatalf("escape error = %v", err)
29 }
30 }
31
32 func TestOpenFileBeneathReadsFromValidatedHandle(t *testing.T) {
33 root := t.TempDir()
34 safe := filepath.Join(root, "safe.txt")
35 outside := filepath.Join(t.TempDir(), "secret.txt")
36 if err := os.WriteFile(safe, []byte("safe"), 0o600); err != nil {
37 t.Fatal(err)
38 }
39 if err := os.WriteFile(outside, []byte("secret"), 0o600); err != nil {
40 t.Fatal(err)
41 }
42 link := filepath.Join(root, "current.txt")
43 if err := os.Symlink(safe, link); err != nil {
44 t.Skipf("symlink unavailable: %v", err)
45 }
46
47 file, err := OpenFileBeneath(root, "current.txt")
48 if err != nil {
49 t.Fatalf("open internal symlink: %v", err)
50 }
51 defer file.Close()
52 if err := os.Remove(link); err != nil {
53 t.Fatal(err)
54 }
55 if err := os.Symlink(outside, link); err != nil {
56 t.Fatal(err)
57 }
58 got, err := io.ReadAll(file)
59 if err != nil {
60 t.Fatal(err)
61 }
62 if string(got) != "safe" {
63 t.Fatalf("read followed swapped path: got %q", got)
64 }
65 }
66
67 func TestOpenFileBeneathRejectsParentTraversal(t *testing.T) {
68 if file, err := OpenFileBeneath(t.TempDir(), filepath.Join("..", "secret")); err == nil {
69 file.Close()
70 t.Fatal("parent traversal was accepted")
71 }
72 }
73
73 lines GO