gitcat
1package server
2
3import (
4 "net/http"
5 "net/http/httptest"
6 "strings"
7 "testing"
8)
9
10// doPush 发起一次推送请求。
11func doPush(srv *Server, target, user, pass string) *httptest.ResponseRecorder {
12 r := httptest.NewRequest(http.MethodPost, target, strings.NewReader("0000"))
13 if user != "" {
14 r.SetBasicAuth(user, pass)
15 }
16 w := httptest.NewRecorder()
17 srv.Handler().ServeHTTP(w, r)
18 return w
19}
20
21// TestPushRequiresRepoOwnership 是 P0-1 的回归测试:任意已登录用户都不得
22// 向他人仓库推送。修复前 bob 能往 alice 的仓库任意推、删分支、删标签。
23func TestPushRequiresRepoOwnership(t *testing.T) {
24 srv, st, alice, bob := newTestServer(t)
25 makeRepo(t, srv, st, "victim", alice)
26
27 for _, target := range []string{
28 "/victim.git/git-receive-pack",
29 "/victim.git/info/refs?service=git-receive-pack",
30 } {
31 if w := doPush(srv, target, bob.Username, testPass); w.Code != http.StatusForbidden {
32 t.Errorf("bob 推送 alice 的仓库(%s)返回 %d,期望 403", target, w.Code)
33 }
34 }
35 // owner 本人必须放行,否则说明权限判断写死了
36 if w := doPush(srv, "/victim.git/git-receive-pack", alice.Username, testPass); w.Code == http.StatusForbidden {
37 t.Error("仓库 owner 自己推送被误拒")
38 }
39}
40
41func TestPushRequiresAuth(t *testing.T) {
42 srv, st, _, _ := newTestServer(t)
43 makeRepo(t, srv, st, "open", nil)
44 w := doPush(srv, "/open.git/git-receive-pack", "", "")
45 if w.Code != http.StatusUnauthorized {
46 t.Fatalf("匿名推送返回 %d,期望 401", w.Code)
47 }
48 if w.Header().Get("WWW-Authenticate") == "" {
49 t.Error("缺少 WWW-Authenticate 挑战头")
50 }
51}
52
53func TestArchivedRepoBlocksPush(t *testing.T) {
54 srv, st, alice, _ := newTestServer(t)
55 makeRepo(t, srv, st, "old", alice)
56 repo, _ := st.RepoByName("old")
57 repo.IsArchived = true
58 if err := st.UpdateRepo(repo); err != nil {
59 t.Fatalf("归档失败: %v", err)
60 }
61 if w := doPush(srv, "/old.git/git-receive-pack", alice.Username, testPass); w.Code != http.StatusForbidden {
62 t.Fatalf("归档仓库推送返回 %d,期望 403", w.Code)
63 }
64}
65
66// TestCloneStaysPublic 确认修复没有把只读克隆也一起拦住。
67func TestCloneStaysPublic(t *testing.T) {
68 srv, st, _, _ := newTestServer(t)
69 makeRepo(t, srv, st, "public", nil)
70 r := httptest.NewRequest(http.MethodGet, "/public.git/info/refs?service=git-upload-pack", nil)
71 w := httptest.NewRecorder()
72 srv.Handler().ServeHTTP(w, r)
73 if w.Code != http.StatusOK {
74 t.Fatalf("匿名 clone 返回 %d,期望 200(只读克隆必须保持公开)", w.Code)
75 }
76}
77
78// TestDisabledUserCannotPush 停用账号不得推送。
79func TestDisabledUserCannotPush(t *testing.T) {
80 srv, st, _, _ := newTestServer(t)
81 makeRepo(t, srv, st, "repo1", nil)
82 bob, _ := st.UserByUsername("bob")
83 if err := st.SetUserFlags(bob.ID, false, true); err != nil {
84 t.Fatalf("停用 bob 失败: %v", err)
85 }
86 if w := doPush(srv, "/repo1.git/git-receive-pack", "bob", testPass); w.Code != http.StatusUnauthorized {
87 t.Fatalf("停用账号推送返回 %d,期望 401", w.Code)
88 }
89}