This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

core / knotserver / sandbox / repofs_test.go
5.4 kB 193 lines
1package sandbox 2 3import ( 4 "os" 5 "path/filepath" 6 "runtime" 7 "testing" 8) 9 10func TestChmodRepoTree(t *testing.T) { 11 root := t.TempDir() 12 13 // build a tree: 14 // root/ 15 // file.txt (0644) 16 // script.sh (0755) 17 // subdir/ 18 // nested.txt (0644) 19 // link -> ../file.txt 20 mustWrite(t, filepath.Join(root, "file.txt"), 0644, "hello") 21 mustWrite(t, filepath.Join(root, "script.sh"), 0755, "#!/bin/sh\n") 22 mustMkdir(t, filepath.Join(root, "subdir"), 0755) 23 mustWrite(t, filepath.Join(root, "subdir", "nested.txt"), 0644, "nested") 24 mustSymlink(t, "../file.txt", filepath.Join(root, "subdir", "link")) 25 26 if err := ChmodRepoTree(root); err != nil { 27 t.Fatalf("ChmodRepoTree: %v", err) 28 } 29 30 cases := []struct { 31 path string 32 wantPerm os.FileMode 33 wantDir bool 34 }{ 35 {root, 0770, true}, 36 {filepath.Join(root, "file.txt"), 0660, false}, 37 {filepath.Join(root, "script.sh"), 0770, false}, 38 {filepath.Join(root, "subdir"), 0770, true}, 39 {filepath.Join(root, "subdir", "nested.txt"), 0660, false}, 40 } 41 for _, c := range cases { 42 info, err := os.Stat(c.path) 43 if err != nil { 44 t.Errorf("stat %s: %v", c.path, err) 45 continue 46 } 47 if got := info.Mode().Perm(); got != c.wantPerm { 48 t.Errorf("%s: perm = %o, want %o", c.path, got, c.wantPerm) 49 } 50 setgid := info.Mode()&os.ModeSetgid != 0 51 if c.wantDir && !setgid { 52 t.Errorf("%s: setgid bit not set on directory", c.path) 53 } 54 if !c.wantDir && setgid { 55 t.Errorf("%s: setgid bit set on non-directory", c.path) 56 } 57 } 58} 59 60func TestChmodRepoTree_SetsSetgidOnExistingDirs(t *testing.T) { 61 // Directories that already exist without the setgid bit should have it 62 // applied by the chmod walk; otherwise, files later created inside them 63 // by sandbox subprocesses would not inherit the directory's group. 64 root := t.TempDir() 65 mustMkdir(t, filepath.Join(root, "objects"), 0755) 66 mustMkdir(t, filepath.Join(root, "refs", "heads"), 0755) 67 68 if err := ChmodRepoTree(root); err != nil { 69 t.Fatalf("ChmodRepoTree: %v", err) 70 } 71 72 for _, p := range []string{root, filepath.Join(root, "objects"), filepath.Join(root, "refs"), filepath.Join(root, "refs", "heads")} { 73 info, err := os.Stat(p) 74 if err != nil { 75 t.Fatalf("stat %s: %v", p, err) 76 } 77 if info.Mode()&os.ModeSetgid == 0 { 78 t.Errorf("%s: setgid bit not set", p) 79 } 80 } 81} 82 83func TestChmodRepoTree_PreservesExecutableBit(t *testing.T) { 84 root := t.TempDir() 85 mustWrite(t, filepath.Join(root, "exec"), 0744, "") 86 mustWrite(t, filepath.Join(root, "noexec"), 0644, "") 87 88 if err := ChmodRepoTree(root); err != nil { 89 t.Fatalf("ChmodRepoTree: %v", err) 90 } 91 92 if got := mode(t, filepath.Join(root, "exec")); got != 0770 { 93 t.Errorf("exec file: mode = %o, want 0770", got) 94 } 95 if got := mode(t, filepath.Join(root, "noexec")); got != 0660 { 96 t.Errorf("noexec file: mode = %o, want 0660", got) 97 } 98} 99 100func TestChownRepoTree_SelfChown(t *testing.T) { 101 // Chowning to our own UID/GID is always a no-op success. This verifies 102 // the walk visits all entries without erroring. 103 root := t.TempDir() 104 mustWrite(t, filepath.Join(root, "a"), 0644, "") 105 mustMkdir(t, filepath.Join(root, "b"), 0755) 106 mustWrite(t, filepath.Join(root, "b", "c"), 0644, "") 107 108 uid := os.Getuid() 109 gid := os.Getgid() 110 if err := ChownRepoTree(root, uid, gid); err != nil { 111 t.Fatalf("ChownRepoTree: %v", err) 112 } 113 114 // verify everything still belongs to us. 115 for _, p := range []string{root, filepath.Join(root, "a"), filepath.Join(root, "b"), filepath.Join(root, "b", "c")} { 116 info, err := os.Stat(p) 117 if err != nil { 118 t.Fatalf("stat %s: %v", p, err) 119 } 120 _ = info 121 } 122} 123 124func TestLookupUIDForRepoPath(t *testing.T) { 125 if runtime.GOOS == "windows" { 126 t.Skip("uid/gid lookup is unix-only") 127 } 128 scan := t.TempDir() 129 repo := filepath.Join(scan, "did:plc:abc") 130 mustMkdir(t, repo, 0700) 131 132 uid, gid, err := LookupUIDForRepoPath(scan, repo) 133 if err != nil { 134 t.Fatalf("LookupUIDForRepoPath: %v", err) 135 } 136 if uid != uint32(os.Getuid()) { 137 t.Errorf("uid = %d, want %d", uid, os.Getuid()) 138 } 139 if gid != uint32(os.Getgid()) { 140 t.Errorf("gid = %d, want %d", gid, os.Getgid()) 141 } 142} 143 144func TestLookupUIDForRepoPath_OutsideScanPath(t *testing.T) { 145 _, _, err := LookupUIDForRepoPath("/home/git", "/etc/passwd") 146 if err == nil { 147 t.Fatal("expected error for path outside scan path, got nil") 148 } 149} 150 151func TestLookupUIDForRepoPath_NonexistentPath(t *testing.T) { 152 scan := t.TempDir() 153 _, _, err := LookupUIDForRepoPath(scan, filepath.Join(scan, "does-not-exist")) 154 if err == nil { 155 t.Fatal("expected error for nonexistent path, got nil") 156 } 157} 158 159// helpers 160 161func mustWrite(t *testing.T, path string, mode os.FileMode, content string) { 162 t.Helper() 163 if err := os.WriteFile(path, []byte(content), mode); err != nil { 164 t.Fatalf("write %s: %v", path, err) 165 } 166 // WriteFile respects existing mode on overwrite; chmod to be sure. 167 if err := os.Chmod(path, mode); err != nil { 168 t.Fatalf("chmod %s: %v", path, err) 169 } 170} 171 172func mustMkdir(t *testing.T, path string, mode os.FileMode) { 173 t.Helper() 174 if err := os.MkdirAll(path, mode); err != nil { 175 t.Fatalf("mkdir %s: %v", path, err) 176 } 177} 178 179func mustSymlink(t *testing.T, target, link string) { 180 t.Helper() 181 if err := os.Symlink(target, link); err != nil { 182 t.Fatalf("symlink %s -> %s: %v", link, target, err) 183 } 184} 185 186func mode(t *testing.T, path string) os.FileMode { 187 t.Helper() 188 info, err := os.Stat(path) 189 if err != nil { 190 t.Fatalf("stat %s: %v", path, err) 191 } 192 return info.Mode().Perm() 193}