cmd/gitbay-runner/home_test.go

f8b976a97290a20d552056a999511f5d27d8e8ec
gitbay/cmd/gitbay-runner/home_test.go history · blame · raw

90 lines · 2821 bytes

 1package main
 2
 3import (
 4	"os"
 5	"path/filepath"
 6	"strings"
 7	"testing"
 8)
 9
10// A trusted build's home is its repository's, kept between builds so
11// tool caches survive: the same repository gets the same directory back,
12// another repository a different one (#184).
13func TestTrustedHomeIsPerRepositoryAndKept(t *testing.T) {
14	work := t.TempDir()
15	a, done, err := buildHome(work, job{ID: 1, Repo: "alice/app", Trusted: true})
16	if err != nil {
17		t.Fatal(err)
18	}
19	done()
20	if _, err := os.Stat(a); err != nil {
21		t.Fatalf("trusted home removed after its build: %v", err)
22	}
23	b, done, err := buildHome(work, job{ID: 2, Repo: "bob/app", Trusted: true})
24	if err != nil {
25		t.Fatal(err)
26	}
27	done()
28	if a == b {
29		t.Fatalf("two repositories share a build home: %s", a)
30	}
31	again, done, _ := buildHome(work, job{ID: 3, Repo: "alice/app", Trusted: true})
32	done()
33	if again != a {
34		t.Fatalf("build home moved between builds: %s then %s", a, again)
35	}
36	for _, dir := range []string{a, b} {
37		rel, err := filepath.Rel(filepath.Join(work, "trusted-home"), dir)
38		if err != nil || rel == "." || strings.HasPrefix(rel, "..") {
39			t.Fatalf("build home %s is not under %s/trusted-home", dir, work)
40		}
41		st, err := os.Stat(dir)
42		if err != nil {
43			t.Fatal(err)
44		}
45		if st.Mode().Perm() != 0o700 {
46			t.Fatalf("build home mode %o, want 0700", st.Mode().Perm())
47		}
48	}
49}
50
51// An untrusted build gets a home of its own, outside the trusted root,
52// removed when the build ends: nothing a fork's build writes reaches a
53// later build of the repository (#255).
54func TestUntrustedHomeIsDisposable(t *testing.T) {
55	work := t.TempDir()
56	trusted, done, err := buildHome(work, job{ID: 1, Repo: "alice/app", Trusted: true})
57	if err != nil {
58		t.Fatal(err)
59	}
60	done()
61	home, done, err := buildHome(work, job{ID: 2, Repo: "alice/app"})
62	if err != nil {
63		t.Fatal(err)
64	}
65	if home == trusted || strings.HasPrefix(home, filepath.Join(work, "trusted-home")) {
66		t.Fatalf("untrusted build got a trusted home: %s", home)
67	}
68	// What the Go module cache leaves behind: read-only directories.
69	cache := filepath.Join(home, "go", "pkg", "mod", "example.com", "m@v1")
70	if err := os.MkdirAll(cache, 0o755); err != nil {
71		t.Fatal(err)
72	}
73	if err := os.WriteFile(filepath.Join(cache, "go.mod"), []byte("module m\n"), 0o444); err != nil {
74		t.Fatal(err)
75	}
76	os.Chmod(cache, 0o555)
77	os.Chmod(filepath.Dir(cache), 0o555)
78	done()
79	if _, err := os.Stat(home); !os.IsNotExist(err) {
80		t.Fatalf("untrusted home left behind: %v", err)
81	}
82}
83
84// A repository path is server-validated, but a trusted home must still
85// never resolve outside the runner's home root.
86func TestBuildHomeRefusesTraversal(t *testing.T) {
87	if _, _, err := buildHome(t.TempDir(), job{Repo: "../../etc", Trusted: true}); err == nil {
88		t.Fatal("a traversing repository path produced a build home")
89	}
90}