Monorepo for Tangled tangled.org
6

Configure Feed

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

1//go:build linux 2 3package sandbox 4 5import ( 6 "os/exec" 7 "strings" 8 "syscall" 9 "testing" 10) 11 12func TestLandlockBackend_Name(t *testing.T) { 13 if (&LandlockBackend{}).Name() != "landlock" { 14 t.Error("Name should return \"landlock\"") 15 } 16} 17 18func TestLandlockBackend_WrapMulti_NoPaths(t *testing.T) { 19 sb := &LandlockBackend{selfExe: "/proc/self/exe"} 20 cmd := exec.Command("git", "status") 21 22 wrapped, err := sb.WrapMulti(nil, cmd) 23 if err != nil { 24 t.Fatalf("WrapMulti: %v", err) 25 } 26 if wrapped != cmd { 27 t.Error("empty paths should return the original cmd unchanged") 28 } 29} 30 31func TestLandlockBackend_WrapMulti_ArgsConstruction(t *testing.T) { 32 sb := &LandlockBackend{selfExe: "/path/to/knot"} 33 cmd := exec.Command("git", "upload-pack", "--stateless-rpc", ".") 34 cmd.Env = []string{"GIT_PROTOCOL=version=2"} 35 cmd.Stdin = strings.NewReader("input") 36 37 wrapped, err := sb.WrapMulti([]string{"/repos/a", "/repos/b"}, cmd) 38 if err != nil { 39 t.Fatalf("WrapMulti: %v", err) 40 } 41 42 // argv[0] is the selfExe 43 if wrapped.Path != "/path/to/knot" { 44 t.Errorf("Path = %q, want %q", wrapped.Path, "/path/to/knot") 45 } 46 47 // argv should be: [knot, sandbox-exec, --repo-path=/repos/a, --repo-path=/repos/b, --, <abs-git>, upload-pack, ...] 48 args := wrapped.Args 49 if len(args) < 6 { 50 t.Fatalf("Args too short: %v", args) 51 } 52 if args[0] != "/path/to/knot" { 53 t.Errorf("Args[0] = %q, want %q", args[0], "/path/to/knot") 54 } 55 if args[1] != "sandbox-exec" { 56 t.Errorf("Args[1] = %q, want %q", args[1], "sandbox-exec") 57 } 58 if args[2] != "--repo-path=/repos/a" { 59 t.Errorf("Args[2] = %q, want --repo-path=/repos/a", args[2]) 60 } 61 if args[3] != "--repo-path=/repos/b" { 62 t.Errorf("Args[3] = %q, want --repo-path=/repos/b", args[3]) 63 } 64 if args[4] != "--" { 65 t.Errorf("Args[4] = %q, want --", args[4]) 66 } 67 // args[5] is the resolved absolute git path; just check it ends with /git 68 if !strings.HasSuffix(args[5], "/git") && args[5] != "git" { 69 t.Errorf("Args[5] = %q, want path ending in /git or bare \"git\"", args[5]) 70 } 71 if got := args[len(args)-1]; got != "." { 72 t.Errorf("last arg = %q, want %q", got, ".") 73 } 74 75 // Dir should be the first repo path so the kernel chdirs there 76 // after setuid, before execve. 77 if wrapped.Dir != "/repos/a" { 78 t.Errorf("Dir = %q, want %q", wrapped.Dir, "/repos/a") 79 } 80 81 // Env propagated 82 if len(wrapped.Env) != 1 || wrapped.Env[0] != "GIT_PROTOCOL=version=2" { 83 t.Errorf("Env = %v, want [GIT_PROTOCOL=version=2]", wrapped.Env) 84 } 85 86 // Stdio propagated 87 if wrapped.Stdin != cmd.Stdin { 88 t.Error("Stdin not propagated to wrapped cmd") 89 } 90} 91 92func TestLandlockBackend_WrapMulti_NoLookupNoCredential(t *testing.T) { 93 sb := &LandlockBackend{selfExe: "/path/to/knot"} // no lookup 94 cmd := exec.Command("git", "status") 95 96 wrapped, err := sb.WrapMulti([]string{"/repos/a"}, cmd) 97 if err != nil { 98 t.Fatalf("WrapMulti: %v", err) 99 } 100 if wrapped.SysProcAttr != nil && wrapped.SysProcAttr.Credential != nil { 101 t.Error("no lookup configured; Credential should not be set") 102 } 103} 104 105func TestLandlockBackend_WrapMulti_LookupSetsCredential(t *testing.T) { 106 // lookup deliberately returns a different gid (the service group) to 107 // confirm that WrapMulti ignores it and uses uid as the primary gid. 108 // see the comment in sandbox_linux.go for why this matters. 109 sb := &LandlockBackend{ 110 selfExe: "/path/to/knot", 111 lookup: func(repoPath string) (uint32, uint32, error) { 112 if repoPath != "/repos/a" { 113 t.Errorf("lookup called with %q, want /repos/a", repoPath) 114 } 115 return 100042, 1234, nil 116 }, 117 } 118 cmd := exec.Command("git", "status") 119 120 wrapped, err := sb.WrapMulti([]string{"/repos/a"}, cmd) 121 if err != nil { 122 t.Fatalf("WrapMulti: %v", err) 123 } 124 if wrapped.SysProcAttr == nil || wrapped.SysProcAttr.Credential == nil { 125 t.Fatal("Credential should be set when lookup returns uid > 0") 126 } 127 cred := wrapped.SysProcAttr.Credential 128 if cred.Uid != 100042 { 129 t.Errorf("Credential.Uid = %d, want 100042", cred.Uid) 130 } 131 if cred.Gid != 100042 { 132 t.Errorf("Credential.Gid = %d, want 100042 (must equal Uid, not lookup's gid 1234)", cred.Gid) 133 } 134 if !cred.NoSetGroups { 135 t.Error("NoSetGroups should be true") 136 } 137} 138 139func TestLandlockBackend_WrapMulti_LookupErrSkipsCredential(t *testing.T) { 140 sb := &LandlockBackend{ 141 selfExe: "/path/to/knot", 142 lookup: func(string) (uint32, uint32, error) { 143 return 0, 0, syscall.ENOENT 144 }, 145 } 146 cmd := exec.Command("git", "status") 147 148 wrapped, err := sb.WrapMulti([]string{"/repos/a"}, cmd) 149 if err != nil { 150 t.Fatalf("WrapMulti: %v", err) 151 } 152 if wrapped.SysProcAttr != nil && wrapped.SysProcAttr.Credential != nil { 153 t.Error("lookup errored; Credential should not be set") 154 } 155} 156 157func TestLandlockBackend_WrapMulti_LookupZeroSkipsCredential(t *testing.T) { 158 // uid == 0 is treated as "don't drop" so we never accidentally drop to 159 // root. Verify Credential isn't set in that case. 160 sb := &LandlockBackend{ 161 selfExe: "/path/to/knot", 162 lookup: func(string) (uint32, uint32, error) { 163 return 0, 0, nil 164 }, 165 } 166 cmd := exec.Command("git", "status") 167 168 wrapped, err := sb.WrapMulti([]string{"/repos/a"}, cmd) 169 if err != nil { 170 t.Fatalf("WrapMulti: %v", err) 171 } 172 if wrapped.SysProcAttr != nil && wrapped.SysProcAttr.Credential != nil { 173 t.Error("lookup returned uid=0; Credential should not be set") 174 } 175}