Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Code

392 commit(s) on main

Merge change request 01M70WES5ZZDJ6RP226WB11BBZ: Let people rename and remove their own passkeys keenan · 1h ago 4b8c9e1d
history blame raw 5.3 KiB
1package hook
2
3// Quotas are the bound a repository's own writers cannot raise. Each check is
4// exercised with the limit set around what the fixture holds, so the refusal is
5// about the quota and nothing else.
6
7import (
8 "bytes"
9 "crypto/sha256"
10 "encoding/hex"
11 "strings"
12 "testing"
13
14 "rodent.software/vole/core/git"
15 "rodent.software/vole/core/lfs"
16 "rodent.software/vole/metadata"
17)
18
19// runRef runs pre-receive for one ref update with the old value a real push would
20// carry, which is what the ref quota counts against.
21func (f *fixture) runRef(cfg Config, old, next, ref string) (int, string) {
22 f.t.Helper()
23 cfg.RepoDir = f.m.Dir
24 if cfg.UsersDB == "" {
25 cfg.UsersDB = f.usersDB
26 }
27 var out bytes.Buffer
28 cfg.In = strings.NewReader(old + " " + next + " " + ref + "\n")
29 cfg.Out = &out
30 return PreReceive(cfg), out.String()
31}
32
33func TestRefQuotaRefusesAPushThatWouldExceedIt(t *testing.T) {
34 t.Parallel()
35 f := newFixture(t, "hello\n")
36 g := f.m.Git()
37 if err := g.UpdateRef("refs/heads/main", f.head); err != nil {
38 t.Fatal(err)
39 }
40 // The repository holds one branch, so a limit of one refuses a second.
41 code, out := f.runRef(Config{Actor: "owner", MaxRefs: 1}, git.ZeroSHA, f.head, "refs/heads/topic")
42 if code == 0 {
43 t.Fatalf("a push over the ref quota was accepted:\n%s", out)
44 }
45 if !strings.Contains(out, "repo_max_refs") {
46 t.Fatalf("the refusal does not name the quota:\n%s", out)
47 }
48 // Moving an existing branch is not a new ref.
49 tree, err := g.MkTree([]git.Entry{})
50 if err != nil {
51 t.Fatal(err)
52 }
53 next, err := g.CommitTree(tree, f.head, "second")
54 if err != nil {
55 t.Fatal(err)
56 }
57 if code, out := f.runRef(Config{Actor: "owner", MaxRefs: 1}, f.head, next, "refs/heads/main"); code != 0 {
58 t.Fatalf("updating an existing branch was counted as a new ref:\n%s", out)
59 }
60}
61
62// metadataTip builds a metadata commit holding n issue files of roughly size bytes.
63func (f *fixture) metadataTip(t *testing.T, n, size int) string {
64 t.Helper()
65 g := f.m.Git()
66 empty, err := g.MkTree(nil)
67 if err != nil {
68 t.Fatal(err)
69 }
70 base, err := g.CommitTree(empty, "", "metadata")
71 if err != nil {
72 t.Fatal(err)
73 }
74 if err := g.UpdateRef(metadata.Ref, base); err != nil {
75 t.Fatal(err)
76 }
77 defer f.m.Git().UpdateRef(metadata.Ref, base)
78 body := "---\ntitle: t\n---\n" + strings.Repeat("x", size) + "\n"
79 var entries []git.Entry
80 for i := 0; i < n; i++ {
81 blob, err := g.HashObject([]byte(body))
82 if err != nil {
83 t.Fatal(err)
84 }
85 tree, err := g.MkTree([]git.Entry{{Mode: "100644", Type: "blob", SHA: blob, Name: "issue.md"}})
86 if err != nil {
87 t.Fatal(err)
88 }
89 entries = append(entries, git.Entry{
90 Mode: "040000", Type: "tree", SHA: tree,
91 Name: "01M5ISSUE000000000000000" + string(rune('A'+i)),
92 })
93 }
94 issues, err := g.MkTree([]git.Entry{{Mode: "040000", Type: "tree", SHA: mustTree(t, g, entries), Name: metadata.IssuesDir}})
95 if err != nil {
96 t.Fatal(err)
97 }
98 tip, err := g.CommitTree(issues, base, "issues")
99 if err != nil {
100 t.Fatal(err)
101 }
102 return tip
103}
104
105func mustTree(t *testing.T, g *git.Repo, entries []git.Entry) string {
106 t.Helper()
107 sha, err := g.MkTree(entries)
108 if err != nil {
109 t.Fatal(err)
110 }
111 return sha
112}
113
114func TestMetadataQuotaCountsEntriesAndBytes(t *testing.T) {
115 t.Parallel()
116 f := newFixture(t, "hello\n")
117 tip := f.metadataTip(t, 2, 100)
118
119 // Two entries are over a limit of one, and the refusal names the quota.
120 code, out := f.runRef(Config{Actor: "owner", MaxMetadataEntries: 1}, git.ZeroSHA, tip, metadata.Ref)
121 if code == 0 || !strings.Contains(out, "repo_max_metadata_entries") {
122 t.Fatalf("the metadata entry quota did not refuse (code %d):\n%s", code, out)
123 }
124
125 // The byte quota catches the same tree when the entry count is generous.
126 code, out = f.runRef(Config{Actor: "owner", MaxMetadataBytes: 10}, git.ZeroSHA, tip, metadata.Ref)
127 if !strings.Contains(out, "repo_max_metadata_bytes") {
128 t.Fatalf("the metadata byte quota did not refuse (code %d):\n%s", code, out)
129 }
130
131 // A generous quota says nothing about metadata.
132 if _, out := f.runRef(Config{Actor: "owner", MaxMetadataEntries: 100, MaxMetadataBytes: 1 << 20},
133 git.ZeroSHA, tip, metadata.Ref); strings.Contains(out, "repo_max_metadata") {
134 t.Fatalf("a push inside the quota was refused by it:\n%s", out)
135 }
136}
137
138func TestRepoSizeQuotaRefusesAFullRepository(t *testing.T) {
139 t.Parallel()
140 f := newFixture(t, strings.Repeat("hello\n", 1024))
141 if err := f.m.Git().UpdateRef("refs/heads/main", f.head); err != nil {
142 t.Fatal(err)
143 }
144 code, out := f.runRef(Config{Actor: "owner", MaxRepoBytes: 1}, f.head, f.head, "refs/heads/main")
145 if code == 0 {
146 t.Fatalf("a repository over its size quota accepted a push:\n%s", out)
147 }
148 if !strings.Contains(out, "repo_max_bytes") {
149 t.Fatalf("the refusal does not name the quota:\n%s", out)
150 }
151}
152
153func TestLFSQuotaRefusesAFullStore(t *testing.T) {
154 t.Parallel()
155 f := newFixture(t, "hello\n")
156 store := lfs.New(t.TempDir(), 0)
157 body := []byte("an object")
158 sum := sha256.Sum256(body)
159 oid := hex.EncodeToString(sum[:])
160 if _, _, err := store.Put(f.m.ID, oid, int64(len(body)), strings.NewReader(string(body))); err != nil {
161 t.Fatal(err)
162 }
163 code, out := f.runRef(Config{Actor: "owner", MaxLFSBytes: 1, LFS: store, RepoID: f.m.ID},
164 git.ZeroSHA, f.head, "refs/heads/main")
165 if code == 0 {
166 t.Fatalf("a repository over its object quota accepted a push:\n%s", out)
167 }
168 if !strings.Contains(out, "repo_max_lfs_bytes") {
169 t.Fatalf("the refusal does not name the quota:\n%s", out)
170 }
171}
172