cmd/gitbay-runner/workdir_test.go

d6d57309d9ddb202b5c9a29ff4f4d22c000f3874
gitbay/cmd/gitbay-runner/workdir_test.go history · blame · raw

94 lines · 2977 bytes

 1package main
 2
 3import (
 4	"fmt"
 5	"os"
 6	"path/filepath"
 7	"strings"
 8	"testing"
 9)
10
11// The default must not be a fixed name inside a world-writable directory:
12// another local user could create it first, and MkdirAll would accept
13// theirs. This is the process that clones repositories and exports build
14// secrets into step environments (go:S5445, #153).
15func TestDefaultWorkdirIsNotInSharedTmp(t *testing.T) {
16	got := defaultWorkdir()
17	cache, err := os.UserCacheDir()
18	if err != nil || cache == "" {
19		t.Skip("no user cache directory on this machine; the tmp fallback is the tested path")
20	}
21	if !strings.HasPrefix(got, cache) {
22		t.Errorf("default workdir %q is not under the user cache %q", got, cache)
23	}
24	if strings.HasPrefix(got, os.TempDir()+string(filepath.Separator)) {
25		t.Errorf("default workdir %q is still inside the shared temp directory", got)
26	}
27}
28
29func TestCheckWorkdirAcceptsAPrivateDirectory(t *testing.T) {
30	dir := filepath.Join(t.TempDir(), "work")
31	if err := os.MkdirAll(dir, 0o700); err != nil {
32		t.Fatal(err)
33	}
34	if err := checkWorkdir(dir); err != nil {
35		t.Fatalf("a private directory this user owns was refused: %v", err)
36	}
37}
38
39// A workspace we own that is merely too permissive is tightened, not
40// refused: every runner before this one made its workspace 0755, and
41// refusing would take the runner down on upgrade over a permission it is
42// entitled to change.
43func TestCheckWorkdirTightensOurOwnDirectory(t *testing.T) {
44	base := t.TempDir()
45	for _, mode := range []os.FileMode{0o755, 0o770, 0o777} {
46		dir := filepath.Join(base, fmt.Sprintf("mode-%o", mode))
47		if err := os.MkdirAll(dir, mode); err != nil {
48			t.Fatal(err)
49		}
50		if err := os.Chmod(dir, mode); err != nil { // MkdirAll applies umask
51			t.Fatal(err)
52		}
53		if err := checkWorkdir(dir); err != nil {
54			t.Fatalf("mode %04o: refused a directory we own: %v", mode, err)
55		}
56		fi, err := os.Stat(dir)
57		if err != nil {
58			t.Fatal(err)
59		}
60		if got := fi.Mode().Perm(); got != 0o700 {
61			t.Errorf("mode %04o was left at %04o, want 0700", mode, got)
62		}
63	}
64}
65
66// What cannot be repaired is refused: a symlink is not ours to correct,
67// and it is what an attacker leaves behind.
68func TestCheckWorkdirRefusesUnsafeDirectories(t *testing.T) {
69	base := t.TempDir()
70
71	target := filepath.Join(base, "elsewhere")
72	if err := os.MkdirAll(target, 0o700); err != nil {
73		t.Fatal(err)
74	}
75	link := filepath.Join(base, "link")
76	if err := os.Symlink(target, link); err != nil {
77		t.Skipf("symlinks unavailable: %v", err)
78	}
79	if err := checkWorkdir(link); err == nil {
80		t.Error("a symlinked workdir was accepted")
81	} else if !strings.Contains(err.Error(), "symlink") {
82		t.Errorf("symlink refusal does not say why: %v", err)
83	}
84}
85
86func TestCheckWorkdirRefusesAFile(t *testing.T) {
87	f := filepath.Join(t.TempDir(), "notadir")
88	if err := os.WriteFile(f, []byte("x"), 0o600); err != nil {
89		t.Fatal(err)
90	}
91	if err := checkWorkdir(f); err == nil {
92		t.Error("a regular file was accepted as a workdir")
93	}
94}